@import url('https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap');

/* ══════════════════════════════════════════════════════════════════════════
   VIGIA — redesign "sala de controle" (2026)
   Substitui o tema medieval por um sistema de monitoramento: base azul-petróleo
   quase preta no escuro, laboratório frio no claro, acento ciano, tipografia
   técnica (Chakra Petch nos títulos, JetBrains Mono em todo número) e geometria
   reta com cantos em bracket de HUD.

   CONTRATO: nenhum nome de classe, id ou estrutura de HTML mudou. app.js,
   shared.js, home.js, bazaar.js, character.js e as 16 páginas continuam
   funcionando sem tocar em nada. Só aparência.

   Fontes: Chakra Petch + JetBrains Mono vêm do @import acima (o <head> das
   páginas carrega Cinzel/Sora e não pode ser editado neste ciclo). Sora
   continua sendo o corpo de texto.
   ══════════════════════════════════════════════════════════════════════════ */

:root{
  /* Diz ao NAVEGADOR qual é o tema. Sem isso ele desenha todo widget nativo na
     variante clara: no tema escuro a barra de rolagem, o quadrado do checkbox, o
     menu aberto do <select> e o realce de autofill saem BRANCOS. Uma declaração
     por bloco de tema — tem que acompanhar as variáveis nos 4 lugares. */
  color-scheme: light;

  --font-display:'Chakra Petch', 'Segoe UI Semibold', system-ui, sans-serif;
  --font-body:'Sora', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --font-mono:'JetBrains Mono', ui-monospace, SFMono-Regular, 'Cascadia Code', Consolas, monospace;

  /* Geometria reta: instrumento, não cartão de blog. */
  --r-xs:2px; --r-sm:3px; --r-md:5px; --r-lg:8px; --r-pill:999px;

  /* ── claro = laboratório (branco frio levemente azulado) ── */
  --bg: oklch(96.8% .008 240);
  --bg-translucent: oklch(96.8% .008 240 / .84);
  --surface: oklch(99.2% .004 240);
  --surface-2: oklch(94.2% .011 240);
  --surface-3: oklch(90% .016 238);
  --text: oklch(23% .028 250);
  --muted: oklch(50% .028 248);
  --border: oklch(86% .016 240);
  --border-strong: oklch(70% .04 235);
  --accent: oklch(52% .15 233);
  --accent-strong: oklch(41% .16 240);
  --accent-soft: oklch(93% .035 228);
  /* nome herdado: hoje é o "realce máximo" do tema, não mais dourado */
  --gold-hi: oklch(61% .16 228);
  --on-accent: oklch(99% .004 240);
  --online: oklch(52% .14 158);
  --online-soft: oklch(93% .05 158);
  --offline: oklch(52% .19 22);
  --offline-soft: oklch(94% .05 22);
  --warn: oklch(56% .13 72);
  --warn-soft: oklch(94% .05 78);
  --chart-2: oklch(50% .18 295);
  --card-gradient: linear-gradient(168deg, oklch(99.5% .003 240), oklch(96.2% .01 240));
  --panel-inset: inset 0 1px 0 oklch(100% 0 0 / .9);
  --shadow-card: 0 1px 2px oklch(40% .03 250 / .1), 0 14px 30px -20px oklch(38% .06 250 / .38);
  --shadow-soft: 0 18px 44px -26px oklch(38% .06 250 / .5);
  /* Peça que PAIRA sobre o conteúdo (lista de busca, hovercard, ficha, menu). Uma sombra
     só, com -26px de spread, é difusa demais para dizer altura — a receita validada no
     menu da navegação tem contato + corpo + difusa, e a luz na aresta de cima. */
  --shadow-flutuante:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(0% 0 0 / .14),
    0 10px 20px -12px oklch(38% .06 250 / .45),
    0 22px 48px -28px oklch(38% .06 250 / .5);
  --shadow-gold: 0 0 0 1px color-mix(in oklch, var(--accent) 45%, transparent), 0 6px 22px -10px color-mix(in oklch, var(--accent) 50%, transparent);
  --thead-grad: linear-gradient(180deg, oklch(95.5% .01 240), oklch(92% .014 238));
  --barbevel: inset 0 1px 0 oklch(100% 0 0 / .7);
  --bar-grad: linear-gradient(90deg, var(--accent), var(--gold-hi));
  --grid-line: oklch(55% .05 240 / .1);
  --voc-knight: oklch(52% .18 25); --voc-paladin: oklch(50% .14 152); --voc-druid: oklch(50% .13 205);
  --voc-sorcerer: oklch(50% .18 295); --voc-monk: oklch(54% .13 60); --voc-none: var(--muted);
  --hero-grad: linear-gradient(150deg, var(--accent-strong), var(--accent) 55%, var(--accent-strong));
}

/* ── escuro = sala de controle ── */
@media (prefers-color-scheme: dark){ :root{
  color-scheme: dark;
  --bg: oklch(15% .024 258);
  --bg-translucent: oklch(15% .024 258 / .82);
  --surface: oklch(19.5% .026 258);
  --surface-2: oklch(23% .028 258);
  --surface-3: oklch(27.5% .032 258);
  --text: oklch(93% .012 250);
  --muted: oklch(67% .026 250);
  --border: oklch(32% .034 255);
  --border-strong: oklch(45% .06 245);
  --accent: oklch(78% .14 205);
  --accent-strong: oklch(87% .13 200);
  --accent-soft: oklch(28% .06 225);
  --gold-hi: oklch(94% .1 195);
  --on-accent: oklch(15% .03 250);
  --online: oklch(80% .17 158);
  --online-soft: oklch(27% .06 158);
  --offline: oklch(70% .19 22);
  --offline-soft: oklch(27% .07 22);
  --warn: oklch(84% .15 82);
  --warn-soft: oklch(29% .06 82);
  --chart-2: oklch(74% .15 292);
  --card-gradient: linear-gradient(168deg, oklch(22% .028 258), oklch(17.5% .024 257));
  --panel-inset: inset 0 1px 0 oklch(75% .05 230 / .14);
  --shadow-card: 0 1px 0 oklch(8% .02 255 / .9), 0 18px 38px -20px oklch(0% 0 0 / .8);
  --shadow-soft: 0 22px 50px -28px oklch(0% 0 0 / .9);
  --shadow-flutuante:
    inset 0 1px 0 oklch(100% 0 0 / .08),
    0 2px 4px oklch(0% 0 0 / .4),
    0 12px 24px -12px oklch(0% 0 0 / .55),
    0 26px 54px -28px oklch(0% 0 0 / .6);
  --shadow-gold: 0 0 0 1px color-mix(in oklch, var(--accent) 40%, transparent), 0 0 30px -8px color-mix(in oklch, var(--accent) 55%, transparent);
  --thead-grad: linear-gradient(180deg, oklch(26% .03 258), oklch(21% .026 257));
  --barbevel: inset 0 1px 0 oklch(100% 0 0 / .14);
  --grid-line: oklch(80% .07 220 / .07);
  --voc-knight: oklch(72% .18 25); --voc-paladin: oklch(78% .16 152); --voc-druid: oklch(78% .13 205);
  --voc-sorcerer: oklch(74% .16 295); --voc-monk: oklch(80% .14 62); --voc-none: var(--muted);
  --hero-grad: linear-gradient(150deg, var(--gold-hi), var(--accent-strong) 45%, var(--accent));
}}

:root[data-theme="dark"]{
  color-scheme: dark;
  --bg: oklch(15% .024 258); --bg-translucent: oklch(15% .024 258 / .82);
  --surface: oklch(19.5% .026 258); --surface-2: oklch(23% .028 258); --surface-3: oklch(27.5% .032 258);
  --text: oklch(93% .012 250); --muted: oklch(67% .026 250);
  --border: oklch(32% .034 255); --border-strong: oklch(45% .06 245);
  --accent: oklch(78% .14 205); --accent-strong: oklch(87% .13 200); --accent-soft: oklch(28% .06 225);
  --gold-hi: oklch(94% .1 195); --on-accent: oklch(15% .03 250);
  --online: oklch(80% .17 158); --online-soft: oklch(27% .06 158);
  --offline: oklch(70% .19 22); --offline-soft: oklch(27% .07 22);
  --warn: oklch(84% .15 82); --warn-soft: oklch(29% .06 82);
  --chart-2: oklch(74% .15 292);
  --card-gradient: linear-gradient(168deg, oklch(22% .028 258), oklch(17.5% .024 257));
  --panel-inset: inset 0 1px 0 oklch(75% .05 230 / .14);
  --shadow-card: 0 1px 0 oklch(8% .02 255 / .9), 0 18px 38px -20px oklch(0% 0 0 / .8);
  --shadow-soft: 0 22px 50px -28px oklch(0% 0 0 / .9);
  --shadow-flutuante:
    inset 0 1px 0 oklch(100% 0 0 / .08),
    0 2px 4px oklch(0% 0 0 / .4),
    0 12px 24px -12px oklch(0% 0 0 / .55),
    0 26px 54px -28px oklch(0% 0 0 / .6);
  --shadow-gold: 0 0 0 1px color-mix(in oklch, var(--accent) 40%, transparent), 0 0 30px -8px color-mix(in oklch, var(--accent) 55%, transparent);
  --thead-grad: linear-gradient(180deg, oklch(26% .03 258), oklch(21% .026 257));
  --barbevel: inset 0 1px 0 oklch(100% 0 0 / .14);
  --grid-line: oklch(80% .07 220 / .07);
  --voc-knight: oklch(72% .18 25); --voc-paladin: oklch(78% .16 152); --voc-druid: oklch(78% .13 205);
  --voc-sorcerer: oklch(74% .16 295); --voc-monk: oklch(80% .14 62); --voc-none: var(--muted);
  --hero-grad: linear-gradient(150deg, var(--gold-hi), var(--accent-strong) 45%, var(--accent));
}

:root[data-theme="light"]{
  color-scheme: light;
  --bg: oklch(96.8% .008 240); --bg-translucent: oklch(96.8% .008 240 / .84);
  --surface: oklch(99.2% .004 240); --surface-2: oklch(94.2% .011 240); --surface-3: oklch(90% .016 238);
  --text: oklch(23% .028 250); --muted: oklch(50% .028 248);
  --border: oklch(86% .016 240); --border-strong: oklch(70% .04 235);
  --accent: oklch(52% .15 233); --accent-strong: oklch(41% .16 240); --accent-soft: oklch(93% .035 228);
  --gold-hi: oklch(61% .16 228); --on-accent: oklch(99% .004 240);
  --online: oklch(52% .14 158); --online-soft: oklch(93% .05 158);
  --offline: oklch(52% .19 22); --offline-soft: oklch(94% .05 22);
  --warn: oklch(56% .13 72); --warn-soft: oklch(94% .05 78);
  --chart-2: oklch(50% .18 295);
  --card-gradient: linear-gradient(168deg, oklch(99.5% .003 240), oklch(96.2% .01 240));
  --panel-inset: inset 0 1px 0 oklch(100% 0 0 / .9);
  --shadow-card: 0 1px 2px oklch(40% .03 250 / .1), 0 14px 30px -20px oklch(38% .06 250 / .38);
  --shadow-soft: 0 18px 44px -26px oklch(38% .06 250 / .5);
  /* Peça que PAIRA sobre o conteúdo (lista de busca, hovercard, ficha, menu). Uma sombra
     só, com -26px de spread, é difusa demais para dizer altura — a receita validada no
     menu da navegação tem contato + corpo + difusa, e a luz na aresta de cima. */
  --shadow-flutuante:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(0% 0 0 / .14),
    0 10px 20px -12px oklch(38% .06 250 / .45),
    0 22px 48px -28px oklch(38% .06 250 / .5);
  --shadow-gold: 0 0 0 1px color-mix(in oklch, var(--accent) 45%, transparent), 0 6px 22px -10px color-mix(in oklch, var(--accent) 50%, transparent);
  --thead-grad: linear-gradient(180deg, oklch(95.5% .01 240), oklch(92% .014 238));
  --barbevel: inset 0 1px 0 oklch(100% 0 0 / .7);
  --grid-line: oklch(55% .05 240 / .1);
  --voc-knight: oklch(52% .18 25); --voc-paladin: oklch(50% .14 152); --voc-druid: oklch(50% .13 205);
  --voc-sorcerer: oklch(50% .18 295); --voc-monk: oklch(54% .13 60); --voc-none: var(--muted);
  --hero-grad: linear-gradient(150deg, var(--accent-strong), var(--accent) 55%, var(--accent-strong));
}

*{ box-sizing:border-box; }
/* MEDIDO em 12/08: com `html,body{height:100%}` o body computava 720 px (a viewport)
   enquanto o conteúdo tinha 8.693 px. `position:sticky` gruda dentro do bloco que contém
   o elemento, então a .topbar — filha direta do body — grudava só até 641 px de rolagem
   (720 da caixa menos os 79 dela) e depois DESLIZAVA PARA FORA da tela, em todas as
   páginas. Medido na régua: rolagem 640 → topo 0; rolagem 700 → topo -59; rolagem
   1200 → topo -559. `min-height` mantém o body cobrindo a viewport quando há pouco
   conteúdo e o deixa crescer quando há muito. Os fundos (body::before/::after e o
   <fundo-3d>) são position:fixed e não dependem dessa altura. */
html{
  height:100%;
  /* Altura real da topbar, MEDIDA na tela: 56 px de logo + .7rem de padding em cima e
     embaixo = 78,4 px. Quem gruda abaixo dela (o thead da tabela) lê daqui em vez de
     repetir o número. */
  --h-topbar:4.9rem;
}
body{ min-height:100%; }
body{
  margin:0; background:var(--bg); color:var(--text);
  font-family:var(--font-body);
  font-variant-numeric:tabular-nums;
  position:relative;
}

/* ── fundo ambiente (todas as páginas) ──────────────────────────────────────
   Duas camadas fixas atrás de tudo: luzes de acento nos cantos + malha técnica.
   A malha fica em ::after e é animada por TRANSFORM (composição na GPU) em vez
   de background-position, que repintaria a tela inteira a cada quadro numa
   página com tabela de 200 linhas.
   Na home o bg3d.js assume o comando e apaga as duas. */
body::before{
  content:""; position:fixed; inset:0; z-index:-2; pointer-events:none;
  background:
    radial-gradient(1000px 520px at 12% -10%, color-mix(in oklch, var(--accent) 20%, transparent), transparent 62%),
    radial-gradient(820px 460px at 90% 0%, color-mix(in oklch, var(--chart-2) 13%, transparent), transparent 64%),
    radial-gradient(ellipse 150% 130% at 50% 45%, transparent 48%, oklch(12% .02 258 / .3) 100%);
}
body::after{
  content:""; position:fixed; z-index:-1; pointer-events:none;
  top:-80px; left:-80px; right:-80px; bottom:-80px;
  background:
    linear-gradient(var(--grid-line) 1px, transparent 1px) 0 0 / 100% 72px,
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px) 0 0 / 72px 100%;
  mask-image: radial-gradient(ellipse 100% 85% at 50% 30%, oklch(0% 0 0) 25%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 100% 85% at 50% 30%, oklch(0% 0 0) 25%, transparent 78%);
  animation: malha-deriva 26s linear infinite;
  will-change: transform;
}
@keyframes malha-deriva{ from{ transform:translate3d(0,0,0); } to{ transform:translate3d(72px,72px,0); } }
/* O `will-change` sai JUNTO com a animação, e não é detalhe: ele promove esta camada a
   textura própria na GPU, e a camada é a viewport inteira mais 80 px de cada lado.
   MEDIDO em 13/08 com a preferência ligada: `animation-name: none` e `will-change:
   transform` convivendo — **4,8 MB reservados para animar coisa nenhuma**.

   É o único `will-change` do site fora do escopo `.tem-3d`, ou seja, o único que também
   vale no celular — justamente onde a memória é mais escassa e onde o 3D nem entra.
   A própria spec pede que `will-change` não fique em elemento que não vai animar. */
@media (prefers-reduced-motion: reduce){ body::after{ animation:none; will-change:auto; } }

::selection{ background:var(--accent-soft); color:var(--accent-strong); }

.icon{ width:1.05em; height:1.05em; vertical-align:-.15em; color:var(--accent); flex:none; }
h1,h2,h3{ font-family:var(--font-display); font-weight:600; text-wrap:balance; letter-spacing:.005em; }
a{ color:var(--accent-strong); }
a:hover{ color:var(--accent); }

/* Todo número em monoespaçado: num painel de monitoramento colunas de dígitos
   alinhados são o que permite comparar linhas sem ler cada uma. */
th.num, td.num, td.rk-value, .card .value, .stat-value, .pulse-tile-value,
.power-value, .hm-value, .voc-value, .exp-value, .delta-pos, .delta-neg,
.countdown, .baz-valor, .baz-caixa-val, .baz-tempo-valor, .rank-medal, .rank-plain,
.baz-ficha-num strong, .w-row-value, .best-day-exp, .integridade-exemplos, .conta-codigo{
  font-family:var(--font-mono); font-variant-numeric:tabular-nums; font-feature-settings:"zero" 1;
}

/* ── moldura padrão dos painéis: caixa reta, filete superior de acento e
      bracket de canto (linguagem de HUD, sem custo de DOM) ── */
.panel, .compare-card, .card, .chart-panel{
  background:var(--card-gradient);
  border:1px solid var(--border);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-card), var(--panel-inset);
  position:relative;
}
.panel::after, .compare-card::after, .chart-panel::after{
  content:""; position:absolute; top:-1px; left:-1px; width:15px; height:15px;
  border-top:1px solid var(--accent); border-left:1px solid var(--accent);
  border-radius:var(--r-lg) 0 0 0; opacity:.6; pointer-events:none;
}
.card::before{
  content:""; position:absolute; right:-1px; bottom:-1px; width:15px; height:15px;
  border-right:1px solid var(--accent); border-bottom:1px solid var(--accent);
  border-radius:0 0 var(--r-lg) 0; opacity:.45; pointer-events:none;
}

/* ── relevo dos painéis (81 deles, em 14 páginas) ──
   Painel NÃO gira como os cartões da home: MEDIDO em 10/08, um painel ocupa boa parte da
   largura e carrega tabela e texto denso — inclinar isso embaça a leitura e enjoa. O que
   dá volume aqui é a LUZ: uma fonte que segue o ponteiro acende a face virada para ela,
   e a borda superior brilha como quina pegando luz. O olho lê relevo sem nada se mexer.

   Só vale com html.tem-3d (mouse fino + a pessoa não pediu menos movimento). */
.tem-3d .panel, .tem-3d .chart-panel, .tem-3d .compare-card{
  transition:box-shadow .25s ease, border-color .25s ease;
}
/* ── a luz precisa CLAREAR, e no tema claro ela escurecia ──
   MEDIDO em 12/08 comparando luminâncias: no tema claro a superfície tem 99,2% e o acento
   52% — o "brilho" era **47,2 pontos MAIS ESCURO** que o painel, ou seja, manchava em vez
   de acender. No escuro a conta se inverte (superfície 19,5%, acento 78%: +58,5 pontos) e
   ali sempre funcionou. A mesma regra produzia efeitos OPOSTOS nos dois temas.
   Cura: a cor da luz vira variável. No claro é branca com uma tinta de acento — clareia,
   como luz de verdade, sem perder a identidade; no escuro segue o acento, que já clareia.

   CALIBRAÇÃO POR COMPOSIÇÃO, não pela cor pura — e foi isso que me pegou na primeira
   tentativa: subi a luminância da cor (52% → 86%) mas junto subi o alfa para 0,5, e o
   resultado COMPOSTO sobre o cartão piorou (94,5% → 92,9%: escurecia mais que antes).
   O que vale é a mistura final. Com branco 85% e alfa 0,25: 0,25×92,8 + 0,75×99,2 =
   **97,6%** — 1,6 ponto abaixo da superfície contra os 4,7 originais, quase neutro, e a
   tinta de acento continua lá. */
:root{
  --luz-cor:color-mix(in oklch, oklch(100% 0 0) 85%, var(--accent)); --luz-alfa:.25;
  /* Os cartões da home têm cor própria por cartão, então lá o clareamento entra como
     PROPORÇÃO DE BRANCO misturada à tinta, e não como cor pronta. */
  --luz-branco:85%; --luz-alfa-card:25%;
}
html[data-theme="dark"]{
  --luz-cor:var(--accent); --luz-alfa:.1;
  --luz-branco:0%; --luz-alfa-card:22%;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --luz-cor:var(--accent); --luz-alfa:.1;
    --luz-branco:0%; --luz-alfa-card:22%;
  }
}

/* ── três sombras que precisavam existir nos DOIS caminhos do tema escuro ──
   MEDIDO em 13/08: o site chega ao tema escuro por dois caminhos — o `data-theme="dark"`
   (a pessoa clicou no botão) e o `prefers-color-scheme` do sistema (nunca clicou). As
   regras `html[data-theme="dark"]` só cobrem o primeiro. Peças que usam VARIÁVEL de
   sombra (`--shadow-card`) atravessam os dois sem problema, porque a variável muda no
   bloco `@media` — foi assim que `.panel` e `.card` passaram no teste e o `.btn-ghost`
   não: a sombra dele estava escrita com valores literais.

   Por isso estas três viram variáveis, como o resto do sistema, em vez de mais uma regra
   `html[data-theme="dark"]` que funcionaria só para metade das pessoas. */
:root{
  --sombra-btn-2rio:
    inset 0 1px 0 oklch(100% 0 0 / .2),
    inset 0 -1px 0 oklch(0% 0 0 / .14),
    0 1px 1px oklch(0% 0 0 / .1);
  --sombra-nav-hover:
    inset 0 1px 0 oklch(100% 0 0 / .4),
    0 1px 2px oklch(38% .06 250 / .16);
  --sombra-th-active:
    inset 0 2px 4px oklch(0% 0 0 / .26),
    0 2px 3px -1px oklch(0% 0 0 / .16);
}
html[data-theme="dark"]{
  --sombra-btn-2rio:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    inset 0 -1px 0 oklch(0% 0 0 / .25),
    0 1px 3px oklch(0% 0 0 / .2);
  --sombra-nav-hover:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 3px oklch(0% 0 0 / .34);
  --sombra-th-active:
    inset 0 2px 4px oklch(0% 0 0 / .5),
    0 2px 3px -1px oklch(0% 0 0 / .3);
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --sombra-btn-2rio:
      inset 0 1px 0 oklch(100% 0 0 / .07),
      inset 0 -1px 0 oklch(0% 0 0 / .25),
      0 1px 3px oklch(0% 0 0 / .2);
    --sombra-nav-hover:
      inset 0 1px 0 oklch(100% 0 0 / .07),
      0 1px 3px oklch(0% 0 0 / .34);
    --sombra-th-active:
      inset 0 2px 4px oklch(0% 0 0 / .5),
      0 2px 3px -1px oklch(0% 0 0 / .3);
  }
}
.tem-3d .panel::before, .tem-3d .chart-panel::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .25s ease;
  /* Duas camadas: um halo largo que sugere a face iluminada, e um filete na quina de
     cima, que é onde a luz bateria primeiro numa caixa real. */
  background:
    radial-gradient(60% 80% at var(--luz-x, 50%) var(--luz-y, 0%),
      color-mix(in oklch, var(--luz-cor) calc(var(--luz-alfa) * 100%), transparent), transparent 70%),
    linear-gradient(to bottom,
      color-mix(in oklch, var(--luz-cor) calc(var(--luz-alfa) * 140%), transparent), transparent 22%);
}
.tem-3d .panel:hover::before, .tem-3d .chart-panel:hover::before{ opacity:1; }
/* A sombra cresce e desce um pouco: a caixa parece ter subido do plano. */
.tem-3d .panel:hover, .tem-3d .chart-panel:hover{
  box-shadow:var(--shadow-soft), var(--panel-inset);
  border-color:color-mix(in oklch, var(--accent) 30%, var(--border));
}

/* O .card é irmão do .panel na moldura padrão, mas tinha ficado de fora da luz — pego
   na medição de 10/08 (`regra3DParaCard: AUSENTE`). São os cartões de resumo do topo
   das páginas ("Mortes Exibidas", "Causa Mais Fatal"), justamente os que o olho procura
   primeiro.

   Usa `::after` porque o `::before` do .card já é o canto decorativo inferior direito —
   sobrescrevê-lo apagaria a moldura. */
.tem-3d .card{ transition:box-shadow .25s ease, border-color .25s ease; }
.tem-3d .card::after{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .25s ease;
  /* Mesma correção do painel: a luz clareia nos dois temas, em vez de manchar no claro. */
  background:
    radial-gradient(60% 80% at var(--luz-x, 50%) var(--luz-y, 0%),
      color-mix(in oklch, var(--luz-cor) calc(var(--luz-alfa) * 100%), transparent), transparent 70%),
    linear-gradient(to bottom,
      color-mix(in oklch, var(--luz-cor) calc(var(--luz-alfa) * 140%), transparent), transparent 22%);
}
.tem-3d .card:hover::after{ opacity:1; }
.tem-3d .card:hover{ box-shadow:var(--shadow-soft), var(--panel-inset); }

/* ── volume nos cartões de resumo ──
   Medido: o `.card` tinha ganhado a LUZ que segue o ponteiro (o `::after` acima) e sombra,
   mas nenhuma profundidade geométrica — `perspective: none`, `transform-style: flat`.
   Iluminação sem relevo acende uma superfície que continua sendo um retângulo.

   Aqui a peça pede camadas mais que qualquer outra: são duas linhas com hierarquia óbvia,
   um rótulo de 9,9 px e um número de 27,2 px. O número É o conteúdo; o rótulo é a legenda
   dele. Essa diferença já existe no tamanho da fonte, e a profundidade só a confirma.

   Vale para as duas grades onde o cartão aparece: `.profile-cards` (mundo, guilda,
   personagem) e `.summary` (bazaar, bazaar-stats, mortes). Não vale para o `.panel`, e
   isso é decisão antiga e deliberada: painel tem tabela e texto denso dentro, e inclinar
   isso atrapalha a leitura. Cartão de resumo tem duas linhas curtas. */
.tem-3d .profile-cards, .tem-3d .summary{
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .profile-cards .card, .tem-3d .summary .card{
  transform-style:preserve-3d;
  will-change:transform;
  /* `overflow` volta a `visible` aqui, e sem isto NADA do 3D interno funciona: um
     `overflow` diferente de `visible` é uma das "grouping properties" da spec e força
     `transform-style: flat`, vencendo o `preserve-3d` declarado logo acima. MEDIDO na
     tela: com `hidden`, o `.value` a Z=20 media 221,8 px — exatamente a largura sem
     transform; com `visible`, 226,3 px. As camadas estavam escritas e não existiam.

     O `container-type: inline-size` do `.card` foi o primeiro suspeito e está inocente:
     as camadas funcionam com ele ativo. Ainda bem, porque ele é quem sustenta o
     `clamp(1.05rem, 13cqi, 1.7rem)` do valor — mexer ali traria de volta o número
     quebrando no meio dos dígitos.

     Liberar o `overflow` só no escopo `.tem-3d` mantém a proteção no caminho padrão
     (celular, quem pediu menos movimento). Antes de tirá-la, medi os filhos de 8 cartões
     em duas páginas, incluindo valores longos como "22.419.846 TC": vazamento de 0,0 px.
     A contenção real de texto grande é o `overflow-wrap: anywhere` do `.value`, que
     continua onde estava. */
  overflow:visible;
}
/* O `transform` do hover precisa vir com `!important` por um motivo específico e MEDIDO:
   `.profile-cards .card` roda a animação `surgir`, que também anima `transform`. Uma
   animação — correndo ou preenchida por `fill-mode: both` — ocupa uma casa mais alta da
   cascata que uma regra normal, então o giro simplesmente não acontecia: a luz acendia e
   o cartão não se mexia.

   Confirmado em laboratório na própria página, com três elementos lado a lado: com a
   animação ativa, a regra normal rendeu `matrix(1,0,0,1,0,6)` (o valor da ANIMAÇÃO) e a
   mesma regra com `!important` rendeu a matriz com a rotação. O controle sem animação
   aceitou a regra normal — ou seja, o conflito é com a animação, não com especificidade.

   Vale para quem NÃO pediu menos movimento: sob `prefers-reduced-motion` o projeto já
   desliga a `surgir`, e aí não há disputa nenhuma. */
.tem-3d .profile-cards .card:hover, .tem-3d .summary .card:hover{
  transition:transform .25s cubic-bezier(.22,.61,.36,1), box-shadow .25s ease, border-color .25s ease;
}
/* O `transform` do hover precisa vir com `!important` por um motivo específico e MEDIDO:
   `.profile-cards .card` roda a animação `surgir`, que também anima `transform`. Uma
   animação — correndo ou preenchida por `fill-mode: both` — ocupa uma casa mais alta da
   cascata que uma regra normal, então o giro simplesmente não acontecia: a luz acendia e
   o cartão não se mexia.

   Confirmado em laboratório na própria página, com três elementos lado a lado: com a
   animação ativa, a regra normal rendeu `matrix(1,0,0,1,0,6)` (o valor da ANIMAÇÃO) e a
   mesma regra com `!important` rendeu a matriz com a rotação. O controle sem animação
   aceitou a regra normal — ou seja, o conflito é com a animação, não com especificidade.

   Vale para quem NÃO pediu menos movimento: sob `prefers-reduced-motion` o projeto já
   desliga a `surgir`, e aí não há disputa nenhuma. */
.tem-giro .profile-cards .card:hover, .tem-giro .summary .card:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) !important; }
/* O número à frente, o rótulo logo atrás. Ambos são estreitos em relação ao cartão (222
   de 257 px), então sobra folga de padding e nada encosta na borda. */
.tem-3d .profile-cards .card .value, .tem-3d .summary .card .value{ transform:translateZ(20px); }
/* O `display:block` no rótulo não é enfeite: metade das páginas escreve o rótulo como
   `<span>` (bazaar, bazaar-stats) e a outra metade como `<div>` (mundo, guilda), e
   `transform` simplesmente NÃO SE APLICA a elemento `display:inline` — está na spec de
   CSS Transforms. Medido: como `inline` o rótulo a Z=10 ficava em 114,3 px, o mesmo valor
   sem transform; como `block`, 259,2 px. Também medido que não mexe no layout: a altura
   do cartão fica em 111,3 px dos dois jeitos, porque o rótulo já ocupava a linha inteira. */
.tem-3d .profile-cards .card .label, .tem-3d .summary .card .label{
  display:block;
  transform:translateZ(10px);
}

@media (prefers-reduced-motion: reduce){
  .tem-3d .profile-cards .card:hover, .tem-3d .summary .card:hover{ transform:none !important; transition:none; }
}

/* ── os cartões de RANKING ganham a mesma profundidade ──
   MEDIDO em 31/08: `.profile-cards .card` tinha 6 regras de 3D e `.rank-card` tinha ZERO.
   As duas grades ficam na mesma ficha de personagem, uma por aba — a de Personagem com
   relevo e a de Rankings completamente chapada. A diferença aparece ao trocar de aba.

   O `.rank-card` já carrega a classe `card`, então bastou dar a ele o mesmo tratamento:
   perspectiva na GRADE (para os cinco cartões dividirem um ponto de fuga só, como na home),
   `preserve-3d` no cartão e as camadas em Z.

   A hierarquia segue o que o olho procura primeiro: a POSIÇÃO (#1) é o número que importa
   e vai mais à frente; os pontos logo atrás; o rótulo por último; o gráfico rente ao
   fundo, porque é contexto, não manchete. */
.tem-3d .rank-grid{
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .rank-card{
  transform-style:preserve-3d;
  /* Mesmo motivo do bloco acima: `overflow` diferente de `visible` é grouping property e
     mataria o preserve-3d — as camadas mediriam 0. */
  overflow:visible;
  transition:transform .25s cubic-bezier(.22,.61,.36,1), box-shadow .25s ease, border-color .25s ease;
}
/* `!important` pela mesma razão já medida no cartão de perfil: `.card` roda a animação
   `surgir`, que também anima `transform` e vence regra normal na cascata. Sem isto o
   cartão acende a luz e não gira. */
.tem-giro .rank-card:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) !important; }
/* `display:block` nos rótulos: `transform` não se aplica a elemento `display:inline`
   (spec de CSS Transforms), e uma camada a Z que não sobe é uma camada que não existe. */
.tem-3d .rank-card .rank-pos{ display:block; transform:translateZ(22px); }
.tem-3d .rank-card .rank-value{ display:block; transform:translateZ(13px); }
.tem-3d .rank-card .label{ display:block; transform:translateZ(7px); }
.tem-3d .rank-card .rank-sparkline{ transform:translateZ(3px); }

@media (prefers-reduced-motion: reduce){
  .tem-3d .rank-card:hover{ transform:none !important; transition:none; }
}
/* ── por que as CAMADAS não são desligadas aqui ──
   MEDIDO em 13/08: sete blocos de reduced-motion zeravam, além do giro, o `translateZ`
   constante das camadas internas — 16 seletores. O giro é movimento e sai; um translateZ
   constante não anima nada, o elemento fica parado, só mais perto do observador. É a mesma
   regra que o site já aplica às sombras desde o ciclo 72 (profundidade fica, movimento
   sai), e ela vinha valendo para a sombra e não para a camada.
   O movimento continua todo desligado, por três caminhos independentes: `iniciarCartoes3D`
   e `iniciarParallaxDoHero` fazem `return` na primeira linha sob esta preferência, e as
   regras `:hover` acima seguem zeradas com `!important`. Sem ponteiro lido, `--fuga-x/y`
   nunca muda, então o `perspective-origin` também fica parado.
   Transbordo foi verificado, não suposto: não existe nenhum @media de LARGURA zerando 3D,
   ou seja, quem não pede reduced-motion já convive com estas camadas a 375 px. */

/* ── a luz do ponteiro sai para quem pediu menos movimento — TODAS as famílias ──
   Até 13/08 só três apagavam a luz aqui (painel, gráfico e cartão de resumo) e as outras
   seis mantinham: metade do sistema fazia uma coisa e metade fazia outra.

   Quem tem razão são as que apagam, e a régua é a mesma que o projeto já usa em outros
   pontos: SOMBRA fica, porque é estática — "profundidade não é animação". A luz do
   ponteiro é o contrário: ela DESLIZA pela superfície acompanhando o cursor, o que é
   movimento contínuo. Se a pessoa liga "reduzir movimento", é exatamente isso que ela
   está pedindo para parar.

   ALCANCE REAL, medido: quem já chega com a preferência ligada nem recebe a classe
   `tem-3d` (o `iniciarCartoes3D` volta antes de adicioná-la), então nenhuma regra daqui
   valeria. Este bloco existe para o caso em que a preferência muda com a página ABERTA —
   aí a classe já está no `<html>`, os listeners já estão registrados e continuariam
   escrevendo `--luz-x`/`--luz-y`. É um cinto de segurança estreito, mas é justamente
   quando alguém acabou de pedir que o movimento pare. */
@media (prefers-reduced-motion: reduce){
  .tem-3d .panel::before, .tem-3d .chart-panel::before, .tem-3d .card::after,
  .tem-3d .home-card::before, .tem-3d .riv-item::before, .tem-3d .guild-card::before,
  .tem-3d .pulse-tile::before, .tem-3d .status-guild-row::before,
  .tem-3d .charm-item::before{ display:none; }
  .tem-3d .panel, .tem-3d .chart-panel, .tem-3d .compare-card, .tem-3d .card{ transition:none; }
}

/* ═══ topbar ═══ */
.topbar{
  display:grid; grid-template-columns:auto 1fr auto; align-items:center;
  padding:.7rem 1.4rem; gap:1rem; position:sticky; top:0; z-index:10;
  background:var(--bg-translucent); backdrop-filter:blur(16px) saturate(1.4); -webkit-backdrop-filter:blur(16px) saturate(1.4);
  border-bottom:1px solid var(--border);
}
/* A barra só projeta sombra quando existe conteúdo correndo por baixo dela (html.rolou,
   marcado por IntersectionObserver em shared.js). No topo da página não há o que
   sombrear — sombra ali seria enfeite afirmando uma profundidade que não existe.

   Não depende de `tem-3d`: isto não é movimento nem exige mouse, é a informação de que a
   barra está acima do conteúdo. Vale também no celular e para quem pediu menos animação. */
.topbar{ transition:box-shadow .22s ease; }
.rolou .topbar{
  box-shadow:
    0 1px 0 color-mix(in oklch, var(--accent) 18%, transparent),
    0 8px 24px -12px oklch(0% 0 0 / .38);
}
html[data-theme="dark"] .rolou .topbar{
  box-shadow:
    0 1px 0 color-mix(in oklch, var(--accent) 22%, transparent),
    0 10px 30px -12px oklch(0% 0 0 / .75);
}
@media (prefers-reduced-motion: reduce){
  .topbar{ transition:none; }
}

/* Filete de sinal sob o cabeçalho — o único "neon" do sistema, e é fino. */
.topbar::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:1px;
  background:linear-gradient(90deg, transparent, var(--accent) 20%, var(--gold-hi) 50%, var(--accent) 80%, transparent);
  opacity:.6; pointer-events:none;
}
.topbar-brand{ grid-column:1; justify-self:start; display:flex; flex-direction:row; align-items:center; gap:.7rem; }

/* ── marca ──────────────────────────────────────────────────────────────────
   assets/logo.png é o brasão original, mantido por decisão. Ele já vem com
   fundo próprio (placa navy + filete dourado), então recebe só a borda e o raio
   do sistema pra ler como emblema nos dois temas em vez de retângulo colado. */
.topbar-brand a{ display:inline-flex; align-items:center; text-decoration:none; }
/* A logo tem fundo próprio (placa navy + filete dourado) e o CSS a trata como EMBLEMA —
   só borda e raio. MEDIDO em 12/08: emblema sem `box-shadow`, ou seja, uma placa colada
   na barra. Foi o último elemento da faixa a ficar de fora do relevo do ciclo 24. */
.site-logo{
  display:block; height:56px; width:auto;
  border-radius:var(--r-md); border:1px solid var(--border);
  transition:border-color .15s ease, box-shadow .15s ease;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .18),
    0 1px 2px oklch(38% .06 250 / .28),
    0 4px 10px -6px oklch(38% .06 250 / .35);
}
/* Mesmo cuidado dos botões da barra: o halo dourado SOMA, senão a placa achata quando o
   ponteiro chega nela. */
.topbar-brand a:hover .site-logo{
  border-color:var(--accent);
  box-shadow:
    var(--shadow-gold),
    inset 0 1px 0 oklch(100% 0 0 / .18),
    0 1px 2px oklch(38% .06 250 / .28);
}
html[data-theme="dark"] .site-logo{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .1),
    0 1px 3px oklch(0% 0 0 / .5),
    0 5px 12px -6px oklch(0% 0 0 / .55);
}
.eyebrow{
  font-family:var(--font-mono); font-size:.6rem; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted); font-weight:500; display:inline-block; line-height:1.2;
  padding-left:.75rem; border-left:1px solid var(--border);
}
.health{ grid-column:3; justify-self:end; display:flex; align-items:center; gap:.6rem; font-size:.83rem; color:var(--muted); }
.dot{ width:.5rem; height:.5rem; border-radius:50%; background:var(--muted); display:inline-block; }
.dot.ok{ background:var(--online); box-shadow:0 0 0 3px var(--online-soft), 0 0 10px var(--online); }
.dot.bad{ background:var(--offline); box-shadow:0 0 0 3px var(--offline-soft), 0 0 10px var(--offline); }
.countdown{
  color:var(--muted); border-left:1px solid var(--border);
  padding-left:.7rem; margin-left:.3rem; font-size:.8rem;
}
.countdown.due{ color:var(--accent-strong); font-weight:600; }
.theme-toggle{
  border:1px solid var(--border); background:var(--surface-2);
  border-radius:var(--r-md); width:2.15rem; height:2.15rem; font-size:1rem; cursor:pointer; line-height:1;
  display:flex; align-items:center; justify-content:center; color:var(--text);
  transition:border-color .15s ease, box-shadow .15s ease;
}
/* ── relevo da barra superior e da navegação ──
   MEDIDO em 12/08: o botão de tema, o de entrar, os 5 gatilhos de menu, o "Início" e a
   tecla Ctrl K estavam TODOS com `box-shadow: none`, enquanto o campo de busca ao lado
   deles já tinha sulco desde o ciclo dos formulários. Resultado: a faixa mais vista do
   site (24 páginas, sempre visível) tinha um único elemento com volume, o campo — e ele
   parecia deslocado no meio de retângulos planos.
   Os dois botões seguem o mesmo relevo de tecla dos botões do corpo. */
.theme-toggle, .auth-button{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    inset 0 -1px 0 oklch(0% 0 0 / .1),
    0 1px 2px oklch(38% .06 250 / .2);
}
/* O halo dourado do hover SOMA com o relevo. Antes ele substituía o box-shadow inteiro;
   com o relevo novo em repouso, isso faria a tecla achatar justo ao ser apontada.
   O `border-color` vem junto porque esta regra CONSOLIDOU as duas antigas
   (`.theme-toggle:hover` e `.auth-button:hover`, que só tinham o halo) — duas regras
   fazendo a mesma coisa em lugares diferentes do arquivo é o que faz um conserto
   parecer aplicado enquanto a versão velha continua ali. */
.theme-toggle:hover, .auth-button:hover{
  border-color:var(--accent);
  box-shadow:
    var(--shadow-gold),
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .2);
}
.theme-toggle:active, .auth-button:active{
  box-shadow:inset 0 2px 4px oklch(0% 0 0 / .28);
}
html[data-theme="dark"] .theme-toggle, html[data-theme="dark"] .auth-button{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .08),
    inset 0 -1px 0 oklch(0% 0 0 / .25),
    0 1px 3px oklch(0% 0 0 / .4);
}

/* ═══ cards de resumo ═══ */
.summary{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(175px,1fr));
  gap:.8rem; padding:1.5rem 1.8rem .2rem;
}
.card{ padding:.9rem 1.05rem 1rem; overflow:hidden; container-type:inline-size; }
.card:hover{ border-color:var(--accent); }
.card .label{
  font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.12em;
  color:var(--muted); font-weight:500;
}
/* clamp com cqi: "2.026.785.214" num card estreito quebrava NO MEIO DOS DÍGITOS
   (o valor precisa de overflow-wrap:anywhere por causa de nome longo), e
   "2.026.785.21 / 4" lê como outro número. A fonte encolhe só quando falta
   largura. Sem suporte a cqi o clamp inteiro é inválido e cai no tamanho base. */
.card .value{
  font-size:clamp(1.05rem, 13cqi, 1.7rem); font-weight:700; margin-top:.35rem; line-height:1.15;
  letter-spacing:-.02em; overflow-wrap:anywhere; color:var(--text);
}
.card .value.online{ color:var(--online); }
.profile-cards{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr));
  gap:.8rem; margin-bottom:1.5rem; align-items:start;
}

/* ═══ seletor de comparação ═══ */
.compare-picker{
  padding:.9rem 1.8rem 0; display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; font-size:.85rem;
}
.compare-picker-label{
  color:var(--muted); font-family:var(--font-mono); font-weight:500;
  text-transform:uppercase; letter-spacing:.1em; font-size:.66rem;
}
.compare-picker-vs{ color:var(--muted); font-size:.8rem; font-family:var(--font-mono); }
.compare-picker select{ font-size:.85rem; }

/* ═══ comparação ═══ */
.comparison{
  padding:1.1rem 1.8rem 1.3rem; display:grid; grid-template-columns:repeat(auto-fit, minmax(300px,1fr)); gap:1rem;
}
.compare-card{ padding:1.2rem 1.35rem; }
.compare-card h3{
  margin:0 0 .9rem; font-size:1.1rem; text-align:center; color:var(--accent-strong);
  letter-spacing:.04em; display:flex; align-items:center; gap:.6rem; justify-content:center;
}
.compare-card h3::before, .compare-card h3::after{
  content:""; flex:1; height:1px; max-width:5.5rem;
  background:linear-gradient(90deg, transparent, var(--border-strong));
}
.compare-card h3::after{ background:linear-gradient(90deg, var(--border-strong), transparent); }
.compare-row{ display:flex; align-items:center; justify-content:space-between; font-size:.82rem; margin:.5rem 0; gap:.6rem; }
.compare-row .compare-label{ color:var(--muted); flex:0 0 auto; }
.compare-row .compare-value{ font-weight:600; }
/* Faixa lateral + fundo suave em vez de cor no texto: o valor já usa cor pra
   outras coisas (delta-pos, accent no poder somado) e pintar por cima
   embaralharia os dois significados. Empate não marca ninguém. */
.compare-row.cmp-win{
  background:color-mix(in oklch, var(--accent-soft) 75%, transparent);
  box-shadow:inset 2px 0 0 var(--accent);
  border-radius:var(--r-sm); padding:.25rem .45rem; margin-left:-.45rem; margin-right:-.45rem;
}
.cmp-edge{
  font-size:.7rem; font-weight:600; color:var(--accent); margin-left:.35rem;
  font-family:var(--font-mono); white-space:nowrap;
}

/* ── barras: trilho fino, preenchimento com brilho de sinal ── */
.bar-track{
  flex:1; height:.4rem; background:var(--surface-3); border-radius:var(--r-pill);
  /* `overflow:visible` e não `hidden`. MEDIDO em 12/08 por auditoria (cruzar toda sombra
     PROJETADA contra ancestrais que cortam): o `overflow:hidden` que estava aqui recortava
     a terceira camada do preenchimento — o brilho de acento `0 0 8px -2px` — em **28
     barras** (18 na home, 10 no perfil de guilda). O efeito estava escrito, era computado
     pelo navegador e descartado na pintura: ninguém nunca o viu.
     Tirar o recorte é seguro porque o `.bar-fill` já carrega o MESMO `border-radius` do
     trilho, então o formato não dependia do corte; e as larguras medidas vão de 85% a
     100%, nenhuma passando disso (o `max-width` abaixo é o cinto de segurança que
     substitui a função protetora do recorte). */
  overflow:visible; margin:0 .6rem; border:none;
  /* O trilho é um SULCO, não um retângulo pintado: MEDIDO em 10/08, ele estava com
     `box-shadow: none` nas 22 barras do Pulso — chapado, enquanto o preenchimento já
     tinha brilho. A sombra interna no alto e a luz fininha embaixo fazem o canal parecer
     escavado, e é isso que dá ao preenchimento algo onde se encaixar. */
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .18),
    inset 0 -1px 0 oklch(100% 0 0 / .12);
}
.bar-fill{
  /* display:block é OBRIGATÓRIO. Cinco arquivos (home, pulso, dashboard, perfil
     de guilda, status) montam a barra como <span>, e elemento inline IGNORA
     width/height — o preenchimento saía com largura real 0 em todas as páginas,
     o que passa por "linha divisória" e por isso ninguém estranhou. */
  display:block; height:100%; border-radius:var(--r-pill);
  /* Sem o `overflow:hidden` do trilho, é este `max-width` que impede um preenchimento
     acima de 100% (vindo do JS) de escapar da barra. */
  max-width:100%;
  background:var(--bar-grad);
  /* Relevo de cilindro DENTRO do sulco: luz correndo no alto, sombra na base. Só sombra
     interna, nunca gradiente de fundo — o JS escreve `background` inline (a cor da
     vocação, do mundo) e qualquer background daqui seria apagado por ele. A sombra
     externa (o brilho que já existia) fica por último. */
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .3),
    inset 0 -1px 1px oklch(0% 0 0 / .22),
    0 0 8px -2px color-mix(in oklch, var(--accent) 70%, transparent);
}

/* ── o mesmo relevo nas outras barras do site ──
   MEDIDO em 10/08: existem CINCO famílias de barra com a mesma anatomia (trilho + <i> de
   preenchimento), e depois do ciclo anterior só a do Pulso tinha relevo. Ficava
   inconsistente — sulco no Pulso, retângulo chapado no Hunt Analyzer ao lado.
     .ha-barra (hunt-analyzer) · .charm-alt-barra (charms) · .bs-prog-barra (bestiário)
     .baz-det-barra (bazar) · .heatmap-bar (dashboard e perfil de guilda)
   Mesma técnica de lá: só sombra interna, porque o preenchimento leva cor própria e um
   background daqui a apagaria. */
/* 13/08: a lista cresceu. `.pd-barra` (proteções do equipamento), `.scan-bar` (progresso
   da varredura no status) e `.w-bar` (comparação entre mundos) tinham a mesma anatomia e
   estavam sem relevo — foram varridas por ASSINATURA (fundo `--surface-3` e raio de
   pastilha), não por nome, que é como as três escaparam das buscas anteriores: elas se
   chamam "bar" e "barra" indistintamente.

   Elas entram AQUI e não em regras próprias. Foi a lição da vez: ao dar relevo a duas
   destas em regras separadas, a minha vinha depois no arquivo com a mesma especificidade,
   vencia a agrupada e entregava UMA camada de preenchimento no lugar de duas. */
.ha-barra, .charm-alt-barra, .bs-prog-barra, .baz-det-barra,
.pd-barra, .scan-bar, .w-bar{
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .18),
    inset 0 -1px 0 oklch(100% 0 0 / .12);
}
.ha-barra > i, .charm-alt-barra > i, .bs-prog-barra > i, .baz-det-barra > i,
.pd-barra > i, .scan-bar > .scan-bar-fill, .w-bar > .w-bar-fill{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .3),
    inset 0 -1px 1px oklch(0% 0 0 / .22);
}
/* A `.w-bar` tem 3 px (as outras têm 5-6) e a luz da aresta de baixo se sobrepõe à sombra
   de cima nessa altura, apagando a direção do sulco. Fica só a sombra, como na barra de
   skill do Bazaar. */
.w-bar{ box-shadow:inset 0 1px 1px oklch(0% 0 0 / .16); }
html[data-theme="dark"] .w-bar{ box-shadow:inset 0 1px 1px oklch(0% 0 0 / .34); }
/* ── o AFUNDAMENTO do clique precisa existir no tema escuro ──
   MEDIDO em 13/08, varrendo estados (`:hover`/`:active`/`:focus`) que declaram sombra com
   valores literais: **19 deles não tinham par no escuro**, e quase todos são o mesmo
   gesto — a tecla afunda ao ser apertada, com uma sombra INTERNA preta de alfa .22 a .30.
   Sobre um fundo já escuro, preto a .22 é praticamente invisível: o site inteiro perdia a
   resposta tátil do clique no tema escuro.

   ERRO MEU QUE ISTO CORRIGE: no ciclo 69 varri "sombras sem par no escuro" e descartei as
   `inset` como "funcionam nos dois temas". Vale para inset BRANCO (luz), não para inset
   PRETO — e eram 19. Corrigi um caso isolado lá (`th.sortable:active`) sem enxergar a
   família inteira.

   A proporção é a mesma daquele caso: alfa x1,9 no escuro (.26 -> .5 lá). Agrupado num
   lugar só, e não em 19 regras espalhadas, pela lição do ciclo 66. */
html[data-theme="dark"] #export-csv:active,
html[data-theme="dark"] .btn-ghost:active:not(:disabled),
html[data-theme="dark"] .notif-toggle:active,
html[data-theme="dark"] .pagination button:active:not(:disabled),
html[data-theme="dark"] .charm-tier:active{
  box-shadow:inset 0 2px 3px oklch(0% 0 0 / .42);
}
html[data-theme="dark"] .baz-toggle:active{ box-shadow:inset 0 2px 3px oklch(0% 0 0 / .46); }
html[data-theme="dark"] .tab:active{ box-shadow:inset 0 2px 3px oklch(0% 0 0 / .5); }
html[data-theme="dark"] .theme-toggle:active,
html[data-theme="dark"] .auth-button:active,
html[data-theme="dark"] .fav-filter-btn:active{
  box-shadow:inset 0 2px 4px oklch(0% 0 0 / .54);
}
html[data-theme="dark"] #watchlist-form button:active,
html[data-theme="dark"] #alertas-form button:active,
html[data-theme="dark"] #login-submit:active,
html[data-theme="dark"] .calc-form button:active,
html[data-theme="dark"] .hero-search button:active{
  box-shadow:inset 0 2px 4px oklch(0% 0 0 / .58);
}
/* Estes dois são o contrário: a luz da aresta de cima no hover. Luz BRANCA sobre fundo
   escuro fica forte demais, então aqui o alfa DESCE — é a mesma direção que o resto do
   sistema usa (bisel de .4 no claro vira .06 no escuro). */
html[data-theme="dark"] .tab:hover{ box-shadow:inset 0 1px 0 oklch(100% 0 0 / .07); }
html[data-theme="dark"] .charm-tier:not(.is-on):hover{ box-shadow:inset 0 1px 0 oklch(100% 0 0 / .1); }

/* ── as caixas de aviso pousam sobre o painel ──
   MEDIDO em 13/08, varrendo por assinatura (borda + fundo próprio + raio, sem sombra):
   cinco caixas de aviso do site tinham a mesma anatomia e nenhuma tinha relevo. São peças
   que INTERROMPEM a leitura para dizer algo — um aviso que não se destaca do painel em
   que está deitado é um parágrafo com borda colorida.

   Numa REGRA AGRUPADA de propósito, e não em cinco regras próprias: foi exatamente isso
   que me fez remover relevo sem querer no ciclo passado, quando escrevi uma regra solta
   que vencia a agrupada das barras. Aqui a lista mora num lugar só desde o começo.

   Sombra fraca de propósito (alfa .12 contra os .18 das peças comuns): a caixa já tem
   borda colorida e fundo de alerta chamando atenção; o relevo aqui só a descola do painel,
   não compete com a cor. */
.charm-aviso, .rk-aviso-fundido, .baz-hist-aviso, .baz-alerta-item, .charm-total-alerta{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .4),
    0 1px 2px oklch(38% .06 250 / .12);
}
html[data-theme="dark"] .charm-aviso, html[data-theme="dark"] .rk-aviso-fundido,
html[data-theme="dark"] .baz-hist-aviso, html[data-theme="dark"] .baz-alerta-item,
html[data-theme="dark"] .charm-total-alerta{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .3);
}

/* O heatmap é a exceção da família: as barras crescem de baixo para cima e NÃO têm <i>
   dentro — a própria barra é o preenchimento. Por isso a luz vai no topo dela (a face que
   receberia luz numa coluna) e o sulco não se aplica. */
.heatmap-bar.has-data{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .32),
    inset -1px 0 0 oklch(0% 0 0 / .14);
}
.power-row{ padding-bottom:.7rem; margin-bottom:.6rem; border-bottom:1px solid var(--border); }
.power-value{ font-size:1.2rem; font-weight:700; color:var(--accent-strong); letter-spacing:-.01em; }
.bazaar-value{ color:var(--warn); font-weight:700; cursor:help; }
.hint{ cursor:help; opacity:.7; font-size:.85em; }
.voc-dist{ margin-top:.9rem; padding-top:.7rem; border-top:1px solid var(--border); }
.voc-dist-label{
  font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.12em;
  color:var(--muted); margin-bottom:.5rem; font-weight:500;
}
.voc-row{ display:flex; align-items:center; gap:.5rem; font-size:.78rem; margin:.3rem 0; }
.voc-row .voc-label{ flex:0 0 4.5rem; color:var(--muted); }
.voc-value{ flex:0 0 1.6rem; text-align:right; font-weight:600; }
/* No perfil de guilda o rótulo é frase ("Entrou esta semana") e o valor traz
   contagem + porcentagem: as medidas acima (feitas pra "Knight") estouravam. */
.voc-row.dist-row .voc-label{ flex:0 0 8rem; }
.voc-row.dist-row .voc-value{ flex:0 0 auto; min-width:4rem; white-space:nowrap; }
/* Mesma história no Pulso: o rótulo carrega mundo + tipo de PvP + level máximo + média,
   e o valor é população de 3 dígitos. Com os 4.5rem de cima o texto quebrava em três
   linhas e a barra virava um traço. */
.voc-row.pulse-row .voc-label{ flex:0 0 14.5rem; color:var(--text); }
.voc-row.pulse-row .voc-value{ flex:0 0 auto; min-width:2.6rem; }
.pulse-locked{ cursor:help; font-size:.9em; }
/* No celular a linha inteira tem menos de 14.5rem úteis: o rótulo passa a ocupar a
   largura toda e a barra desce pra segunda linha, em vez de sumir. */
@media (max-width:560px){
  .voc-row.pulse-row{ flex-wrap:wrap; }
  .voc-row.pulse-row .voc-label{ flex:1 1 100%; }
  .voc-row.pulse-row .bar-track{ flex:1 1 auto; }
}

.highscore-mini-grid{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(70px, 1fr)); gap:.7rem;
  margin-top:.9rem; padding-top:.7rem; border-top:1px solid var(--border);
}
.highscore-mini{ display:flex; flex-direction:column; gap:.15rem; }
.hm-label{ font-family:var(--font-mono); font-size:.6rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); }
.hm-value{ font-size:.95rem; font-weight:700; color:var(--accent-strong); }
.hm-count{ font-size:.7rem; color:var(--muted); }

/* ═══ watchlist ═══ */
.watchlist{ padding:0 1.8rem 1.3rem; }
.watchlist-header{ display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:.6rem; }
.watchlist-header h2{ font-size:1.05rem; margin:0; display:flex; align-items:center; gap:.5rem; }
#watchlist-form{ display:flex; gap:.4rem; flex-wrap:wrap; }
#watchlist-form[hidden]{ display:none; }
#watchlist-form input, #alertas-form input{
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r-md);
  padding:.48rem .7rem; color:var(--text); font-size:.82rem;
}

/* Foco visível só na navegação por teclado. */
.tab:focus-visible, .chip button:focus-visible, #export-csv:focus-visible,
.theme-toggle:focus-visible, .col-picker summary:focus-visible, th.sortable:focus-visible,
#clear-filters:focus-visible, .name-link:focus-visible{
  outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-sm);
}

/* ── botão primário do sistema ── */
#watchlist-form button, #alertas-form button, .calc-form button, #login-submit, .hero-search button{
  border:1px solid transparent; background:var(--accent); color:var(--on-accent);
  border-radius:var(--r-md); padding:.48rem 1.1rem; font-size:.78rem; font-weight:700; cursor:pointer;
  font-family:var(--font-display); letter-spacing:.09em; text-transform:uppercase;
  transition:box-shadow .15s ease, transform .12s ease, filter .15s ease;
}
/* O halo dourado SOMA com o relevo de tecla que estes botões recebem logo abaixo. Antes
   ele substituía o box-shadow inteiro, e os CINCO botões primários do sistema — os de
   AÇÃO ("Buscar", "Calcular", "Adicionar alvo", "Entrar") — achatavam justo no instante
   em que o ponteiro chegava neles. Achado em 12/08 por auditoria do CSSOM (cruzar toda
   regra `:hover` que define box-shadow SEM inset contra a base que TEM inset) e
   confirmado com ponteiro real no botão Buscar: 2 camadas, `temRelevo: false`. */
#watchlist-form button:hover, #alertas-form button:hover, .calc-form button:hover,
#login-submit:hover, .hero-search button:hover{
  box-shadow:
    var(--shadow-gold),
    inset 0 1px 0 oklch(100% 0 0 / .28),
    inset 0 -1px 0 oklch(0% 0 0 / .22);
  filter:brightness(1.08);
}

/* ── botão com relevo de tecla ──
   MEDIDO em 10/08 na página do bazar: dos 42 botões do DOM, o tipo mais comum (19
   deles) tinha `box-shadow: none` — chapado. Botão é o elemento que a pessoa vai
   APERTAR, e um relevo diz isso antes de qualquer texto.

   Bisel em repouso (luz na aresta de cima, sombra na de baixo) e, ao pressionar, o
   relevo se INVERTE: a tecla afunda de verdade em vez de só escorregar 1 px. O
   deslocamento continua, mas agora acompanhado da sombra que o justifica. */
#watchlist-form button, #alertas-form button, .calc-form button, #login-submit, .hero-search button{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .28),
    inset 0 -1px 0 oklch(0% 0 0 / .22),
    0 1px 2px oklch(0% 0 0 / .18);
}
#watchlist-form button:active, #alertas-form button:active, #login-submit:active,
.calc-form button:active, .hero-search button:active{
  transform:translateY(1px);
  /* Afundada: a luz sai de cima e vira sombra interna; a sombra projetada some, porque
     a peça encostou na superfície. */
  box-shadow:
    inset 0 2px 4px oklch(0% 0 0 / .3),
    inset 0 1px 0 oklch(0% 0 0 / .18);
  filter:brightness(.97);
}
#alertas-form button[disabled]{ opacity:.55; cursor:progress; }
.watchlist-error{ color:var(--offline); font-size:.82rem; margin:.5rem 0 0; }
/* Mesmo parágrafo serve de confirmação: cor é o que separa erro de sucesso. */
.watchlist-error.ok{ color:var(--online); }

/* ═══ alertas no Discord ═══ */
#alertas-form{ display:flex; gap:.4rem; flex-wrap:wrap; margin-top:.7rem; }
#alertas-form input{ flex:1 1 22rem; min-width:12rem; font-family:var(--font-mono); font-size:.78rem; }
#alertas-form button[hidden]{ display:none; }
#alertas-form button.danger{
  background:none; color:var(--offline); border-color:var(--border);
  box-shadow:none;
}
#alertas-form button.danger:hover{ border-color:var(--offline); box-shadow:none; filter:none; }
.alertas-ajuda{ color:var(--muted); font-size:.82rem; margin:.5rem 0 0; max-width:62ch; line-height:1.55; }
.alertas-ajuda a{ color:var(--accent-strong); }
.alertas-estado{
  font-family:var(--font-mono); font-size:.66rem; letter-spacing:.1em; text-transform:uppercase; font-weight:500;
  color:var(--muted); border:1px solid var(--border); border-radius:var(--r-pill);
  padding:.16rem .6rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%;
}
.alertas-estado.ligado{ color:var(--online); border-color:color-mix(in oklch, var(--online) 45%, transparent); }
.alertas-nivel{ border:1px solid var(--border); border-radius:var(--r-md); padding:.6rem .9rem .75rem; margin:.85rem 0 0; }
.alertas-nivel legend{ font-family:var(--font-mono); font-size:.66rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); padding:0 .35rem; }
.alertas-nivel label{ display:flex; align-items:baseline; gap:.5rem; font-size:.84rem; padding:.2rem 0; cursor:pointer; }
.alertas-nivel label strong{ color:var(--text); }
.alertas-nivel label span{ color:var(--muted); }
.alertas-nivel[disabled]{ opacity:.5; }
.alertas-nivel[disabled] label{ cursor:not-allowed; }

.watchlist-chips{ display:flex; flex-wrap:wrap; gap:.55rem; margin-top:.8rem; }
.watchlist-chips:empty::before{ content:"Nenhum alvo ainda — adicione um nome acima."; color:var(--muted); font-size:.82rem; }
/* Chip é uma pastilha física pousada no painel — e a mesma base serve às cartas de
   guilda (`.chip.guild-card`), que são 30 por página no diretório. MEDIDO em 12/08:
   `box-shadow: none` nas 30, ou seja, retângulos pintados com um contorno de 1 px.
   Aqui a peça ganha aresta de cima iluminada e sombra de contato embaixo. */
.chip{
  display:flex; align-items:center; gap:.5rem; background:var(--surface-2); border:1px solid var(--border);
  border-radius:var(--r-md); padding:.4rem .5rem .4rem .8rem; font-size:.82rem;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .18);
  transition:border-color .15s ease, box-shadow .18s ease, transform .18s ease;
}
.chip:hover{
  border-color:var(--accent);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .55),
    0 2px 4px oklch(38% .06 250 / .2),
    0 8px 16px -10px oklch(38% .06 250 / .45);
}
/* O levantar de 1 px é movimento, então fica fora do alcance de quem pediu menos dele.
   A sombra maior no hover continua valendo para todo mundo: profundidade não é animação. */
@media (prefers-reduced-motion: no-preference){
  .chip:hover{ transform:translateY(-1px); }
}
html[data-theme="dark"] .chip{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 2px oklch(0% 0 0 / .35);
}
/* MEDIDO em 13/08, comparando quanto o hover ELEVA a peça em cada tema (energia da sombra
   projetada no hover dividida pela do estado normal): no claro o chip subia 3,6x e aqui
   só 2,8x. O reforço de tema estava certo nos dois estados isolados — o que estava torto
   era o SALTO entre eles, e por isso nenhuma medição anterior tinha pego.
   Foi a única peça do site com esse desvio: as outras 46 pares mantêm a mesma elevação
   nos dois temas. Aqui os alfas sobem para devolver a proporção do claro. */
html[data-theme="dark"] .chip:hover{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .1),
    0 2px 5px oklch(0% 0 0 / .55),
    0 10px 20px -10px oklch(0% 0 0 / .72);
}
.chip b{ color:var(--accent-strong); font-weight:600; letter-spacing:.01em; }

/* Cartão do diretório de guildas: brasão à esquerda, texto à direita. Antes o layout
   vinha de um style="" no JS (coluna, max-width 22rem); virou classe porque agora tem
   duas colunas e um estado (com logo / sem logo) que o CSS precisa enxergar. */
.guild-card{ align-items:flex-start; gap:.7rem; max-width:24rem; padding:.6rem .7rem; }
.guild-card-corpo{ display:flex; flex-direction:column; align-items:flex-start; gap:.15rem; min-width:0; }
.guild-card-corpo > *{ max-width:100%; }
/* O selo é o padrão (92,6% das guildas não têm logo no RubinOT), não o caso de exceção:
   a inicial dá âncora de leitura numa lista alfabética de 3.525 nomes. */
/* O brasão é um emblema de 40×40 embutido na carta, e emblema se lê pelo bisel — a mesma
   lógica da medalha do pódio. MEDIDO: os 30 estavam com `box-shadow: none`. Aqui o sulco
   diz que a peça está ENCAIXADA na carta (luz na aresta de baixo, sombra na de cima),
   e não colada por cima dela. */
.guild-brasao{
  flex:0 0 2.5rem; width:2.5rem; height:2.5rem; display:grid; place-items:center; position:relative;
  border-radius:var(--r-sm); border:1px solid var(--border); background:var(--surface-3);
  font-family:var(--font-display, inherit); font-size:1.1rem; font-weight:700; color:var(--accent-strong);
  overflow:hidden;
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .16),
    inset 0 -1px 0 oklch(100% 0 0 / .35);
  text-shadow:0 1px 0 oklch(100% 0 0 / .4);
}
html[data-theme="dark"] .guild-brasao{
  box-shadow:
    inset 0 1px 3px oklch(0% 0 0 / .38),
    inset 0 -1px 0 oklch(100% 0 0 / .06);
  text-shadow:0 1px 0 oklch(0% 0 0 / .5);
}
/* A logo cobre o selo em vez de substituí-lo no DOM: se o CDN falhar, o listener de
   'error' remove só a <img> e a inicial já está lá embaixo, sem novo reflow. */
.guild-brasao-img img{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; background:var(--surface-3); }
/* ── volume nas cartas de guilda ──
   Medido no diretório: 30 cartas de 384×132 com sombra boa e `transform-style: flat`,
   `perspective: none`. Tinham relevo de superfície (aresta iluminada, sombra de contato)
   e nenhuma profundidade interna.

   A perspectiva mora na grade, como na home e na lista de rivalidades. O `:has()` é o que
   mantém isto restrito ao diretório: `.watchlist-chips` também guarda os chips comuns de
   alvo em outras páginas, e aqueles são pastilhas de uma linha, sem camada nenhuma para
   revelar. Onde `:has()` não existir, nada acontece — que é o mesmo que hoje. */
.tem-3d .watchlist-chips:has(> .guild-card){
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .guild-card{
  transform-style:preserve-3d;
  will-change:transform;
}
/* O `.chip` base já anima `transform .18s`, mas com a curva linear de uma pastilha que só
   sobe 1 px. Girar pede a mesma curva dos outros cartões, senão o giro chega antes da
   elevação e a carta parece torcer sozinha. */
.tem-3d .guild-card:hover{
  transition:border-color .15s ease, box-shadow .18s ease, transform .25s cubic-bezier(.22,.61,.36,1);
}
/* O `.chip` base já anima `transform .18s`, mas com a curva linear de uma pastilha que só
   sobe 1 px. Girar pede a mesma curva dos outros cartões, senão o giro chega antes da
   elevação e a carta parece torcer sozinha. */
.tem-giro .guild-card:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }
/* Invólucro sem aparência própria que PRECISA entrar no 3D: um `flat` no meio da cadeia
   achata tudo abaixo dele. Foi exatamente o que aconteceu com `.riv-corpo` no ciclo 45 —
   três camadas escritas no CSS que mediam 0,00% na tela. */
.tem-3d .guild-card-corpo{ transform-style:preserve-3d; }
/* O nome é o que a pessoa procura numa lista de 3.525 guildas, então é o que vem à
   frente; o resto acompanha na ordem em que se lê. */
.tem-3d .guild-card-corpo > b{ transform:translateZ(18px); }
.tem-3d .guild-card .note{ transform:translateZ(12px); }
.tem-3d .guild-card .guild-stats{ transform:translateZ(9px); }
.tem-3d .guild-card-corpo > .muted{ transform:translateZ(5px); }
/* O brasão fica NO PLANO de propósito, e é o único elemento aqui que não sobe. O bisel
   dele (luz na aresta de baixo, sombra na de cima) diz que a peça está encaixada na
   carta, como um selo prensado — levantá-la contradiria o próprio relevo que ela tem.
   Também é o que mais ganharia deslocamento lateral: mora a 159,8 px do centro. */

/* Luz do ponteiro na face inclinada. Mesma fórmula dos outros cartões, incluindo o
   `--luz-branco`, que existe porque no tema claro uma luz de acento pura escurece. */
.tem-3d .guild-card{ position:relative; }
.tem-3d .guild-card::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .2s ease;
  background:radial-gradient(circle at var(--luz-x, 50%) var(--luz-y, 50%),
    color-mix(in oklch,
      color-mix(in oklch, oklch(100% 0 0) var(--luz-branco, 0%), var(--accent))
      var(--luz-alfa-card, 22%), transparent),
    transparent 60%);
}
.tem-3d .guild-card:hover::before{ opacity:1; }

@media (prefers-reduced-motion: reduce){
  /* Só o giro sai; as camadas em translateZ ficam (ver o bloco do .summary, linha ~476). */
  .tem-3d .guild-card, .tem-3d .guild-card:hover{ transform:none; transition:none; }
}

/* Em tela estreita a carta ocupa a largura inteira e não sobra folga lateral nenhuma —
   aí QUALQUER projeção 3D vira rolagem horizontal. Medido a 375 px: o `rotateX` sozinho
   leva o scrollWidth de 375 para 376, porque inclinar em torno do eixo horizontal empurra
   a borda de baixo para a frente e alarga a projeção. O `rotateY` não tem esse efeito
   (encolhe 1 px), e por isso o pior caso é o ponteiro no alto ou embaixo, onde não há
   giro em Y para compensar.

   Na prática essa combinação quase não acontece: em celular `pointer: fine` é falso e o
   `tem-3d` nem entra. Isto existe para o tablet com mouse, que passa no teste do ponteiro
   e cai numa largura de telefone. A elevação e as camadas continuam — o que sai é só o
   giro, que é justamente o que não cabe. */
@media (max-width: 560px){
  .tem-giro .guild-card:hover{ transform:translateY(-2px); }
}

.chip .note{ color:var(--muted); font-size:.76rem; }
.chip button[data-remove], .chip button[data-remove-guild]{
  border:1px solid var(--border); background:var(--surface-3); color:var(--muted); border-radius:var(--r-sm);
  width:1.35rem; height:1.35rem; cursor:pointer; line-height:1; font-size:.8rem;
  display:inline-flex; align-items:center; justify-content:center;
}
.chip button[data-remove]:hover, .chip button[data-remove-guild]:hover{ background:var(--offline-soft); color:var(--offline); border-color:var(--offline); }
.star{ color:var(--accent); margin-right:.3rem; }

/* ═══ controles / abas ═══ */
.controls{
  display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap;
  gap:.8rem; padding:0 1.8rem 1.1rem;
}
/* Um seletor de abas é fisicamente uma CALHA com uma pastilha correndo dentro dela.
   MEDIDO em 12/08: a calha era chapada (fundo + borda de 1 px) e a pastilha ativa tinha
   só `0 0 14px -6px` — um brilho. Brilho é luz sem volume: nada dizia que a peça está
   ACIMA do trilho, então o conjunto lia como dois retângulos pintados lado a lado.
   Aqui o relevo vem de box-shadow, não de transform: a pastilha vive dentro de 3 px de
   padding e qualquer deslocamento a faria vazar da calha. */
.tabs{
  display:inline-flex; gap:2px; padding:3px; border-radius:var(--r-md);
  background:var(--surface-2); border:1px solid var(--border);
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .16),        /* a calha é escavada: sombra na borda de cima */
    inset 0 -1px 0 oklch(100% 0 0 / .1);        /* e a de baixo devolve luz */
}
.tab{
  border:none; background:none; color:var(--muted); border-radius:var(--r-sm);
  padding:.42rem 1rem; font-size:.78rem; cursor:pointer;
  font-family:var(--font-display); font-weight:600; letter-spacing:.06em; text-transform:uppercase;
  transition:color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.tab:hover{
  color:var(--text); background:color-mix(in oklch, var(--surface-3) 70%, transparent);
  box-shadow:inset 0 1px 0 oklch(100% 0 0 / .18);   /* encosta o dedo e a peça já pega luz */
}
/* Duas grafias porque as duas existem no HTML: dashboard.html marca `active` e
   bazaar-stats.html marca `ativo`. MEDIDO na tela: com só `.tab.active` no CSS, as duas
   abas do Bazaar saíam IDÊNTICAS (mesmo fundo transparente, mesma cor, mesmo peso) —
   quem usa leitor de tela sabia qual estava selecionada pelo aria-selected, quem OLHAVA
   não sabia. Aceitar as duas conserta a tela sem tocar em HTML nem em JS. */
.tab.active, .tab.ativo{
  background:var(--accent); color:var(--on-accent); font-weight:700;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .3),         /* aresta de cima da pastilha, no claro */
    inset 0 -1px 0 oklch(0% 0 0 / .22),         /* aresta de baixo, na sombra */
    0 1px 2px oklch(0% 0 0 / .28),              /* a peça projeta sombra na calha */
    0 0 14px -6px var(--accent);                /* o brilho de accent que já existia */
}
/* Pressionar afunda: a mesma peça, agora abaixo da linha da calha. */
.tab:active{ box-shadow:inset 0 2px 3px oklch(0% 0 0 / .26); }
/* No escuro a calha precisa ser mais funda e a pastilha projetar mais, senão o relevo
   some contra um fundo que já é escuro. */
html[data-theme="dark"] .tabs{
  box-shadow:
    inset 0 2px 3px oklch(0% 0 0 / .34),
    inset 0 -1px 0 oklch(100% 0 0 / .06);
}
html[data-theme="dark"] .tab.active, html[data-theme="dark"] .tab.ativo{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .26),
    inset 0 -1px 0 oklch(0% 0 0 / .3),
    0 2px 4px oklch(0% 0 0 / .42),
    0 0 14px -6px var(--accent);
}
.controls-right{ display:flex; align-items:center; gap:1.1rem; flex-wrap:wrap; }
.toggle{ display:flex; align-items:center; gap:.4rem; font-size:.85rem; color:var(--muted); cursor:pointer; }
.toggle input{ accent-color:var(--accent); }
.legend{
  flex-basis:100%; font-size:.78rem; color:var(--muted); margin:.6rem 0 0; display:flex; align-items:center; gap:.4rem; flex-wrap:wrap;
}
.legend-dot{ width:.7rem; height:.7rem; border-radius:var(--r-xs); background:var(--online-soft); border:1px solid var(--online); display:inline-block; }
.legend-dot.dot-green{ background:var(--online-soft); border-color:var(--online); }
.legend-dot.dot-yellow{ background:var(--warn-soft); border-color:var(--warn); margin-left:.5rem; }
#search{
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r-md);
  padding:.5rem .8rem; color:var(--text); font-size:.85rem; min-width:200px;
}

.col-picker{ position:relative; }
.col-picker summary{
  list-style:none; cursor:pointer; border:1px solid var(--border);
  background:var(--surface-2); border-radius:var(--r-md);
  padding:.5rem .9rem; font-size:.82rem; color:var(--muted); user-select:none;
}
.col-picker summary::-webkit-details-marker{ display:none; }
.col-picker[open] summary{ border-color:var(--accent); color:var(--accent-strong); }
.col-picker-menu{
  position:absolute; top:calc(100% + .4rem); right:0; z-index:20; min-width:11rem;
  background:var(--surface); border:1px solid var(--border-strong); border-radius:var(--r-md);
  padding:.5rem; box-shadow:var(--shadow-flutuante); display:flex; flex-direction:column; gap:.2rem;
}
.col-picker-menu label{ display:flex; align-items:center; gap:.5rem; font-size:.82rem; cursor:pointer; padding:.25rem .35rem; border-radius:var(--r-sm); }
.col-picker-menu label:hover{ background:var(--surface-2); }
.col-picker-menu input{ accent-color:var(--accent); }

/* ═══ navegação ═══ */
.site-nav{
  display:flex; justify-content:flex-start; gap:.15rem; flex-wrap:wrap;
  padding:.5rem 1.4rem .6rem; border-bottom:1px solid var(--border);
}
.site-nav .col-picker summary, .nav-home{
  border:1px solid transparent; background:none;
  border-radius:var(--r-md); padding:.5rem .95rem; font-size:.84rem; font-family:var(--font-body);
  letter-spacing:0; text-transform:none; color:var(--muted); position:relative;
}
/* Os gatilhos nascem transparentes (sem fundo), então o relevo só faz sentido quando o
   fundo aparece — no hover. */
/* A sombra projetada do estado claro usa um azul-acinzentado calibrado para fundo claro
   (`oklch(38% .06 250)`); sobre a topbar escura ela praticamente não existe. A variável
   `--sombra-nav-hover` troca por preto e sobe o alfa nos dois caminhos do tema escuro. */
.site-nav .col-picker summary:hover, .nav-home:hover{
  background:var(--surface-2); color:var(--text);
  box-shadow:var(--sombra-nav-hover);
}
/* Menu ABERTO = tecla pressionada. É a leitura física do estado: enquanto a folha está
   caída ali embaixo, o botão que a soltou continua afundado. */
.site-nav .col-picker[open] summary{
  background:var(--surface-2); border-color:var(--border); color:var(--accent-strong);
  box-shadow:inset 0 2px 3px oklch(0% 0 0 / .16);
}
html[data-theme="dark"] .site-nav .col-picker[open] summary{
  box-shadow:inset 0 2px 4px oklch(0% 0 0 / .4);
}
/* .col-picker-menu (base) ancora pela direita — certo pro seletor de colunas,
   errado aqui: com os itens à esquerda, um menu de 190px crescendo pra esquerda
   estourava a viewport. Centralizar sob o próprio gatilho resolve. */
/* O menu suspenso é o elemento que mais pede altura da página inteira: 190×190 px de
   folha pairando sobre o conteúdo. MEDIDO em 12/08: tinha UMA camada de sombra
   (0 18px 44px -26px) — com -26px de spread ela é tão difusa que quase não chega ao
   papel, e o menu lia como um retângulo colado, não suspenso. E abria SECO
   (animation-name: none): aparecia inteiro, do nada, sem dizer de onde veio. */
.site-nav-menu{
  display:flex; flex-direction:column; min-width:190px;
  left:50%; right:auto; transform:translateX(-50%);
  border-radius:var(--r-md); padding:.4rem;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),                 /* a aresta de cima da folha pega luz */
    0 1px 2px oklch(0% 0 0 / .14),                      /* sombra de contato: ancora a peça */
    0 10px 20px -12px oklch(38% .06 250 / .45),         /* corpo da sombra */
    0 22px 48px -28px oklch(38% .06 250 / .5);          /* a difusa que já existia */
  transform-origin:50% 0;                               /* a dobradiça fica na borda de cima */
  animation:nav-menu-abre .14s cubic-bezier(.2,.8,.3,1);
}
/* Uma tampa girando na dobradiça de cima. A perspectiva vai DENTRO do transform (e não
   no pai) de propósito: a ≤720px o .col-picker vira position:static e a origem da
   perspectiva do pai passaria a cair longe do menu, torcendo a folha de lado.
   translateX(-50%) fica na frente para a centralização não depender do giro. */
@keyframes nav-menu-abre{
  from{ opacity:0; transform:translateX(-50%) perspective(700px) rotateX(-12deg) translateY(-3px); }
  to  { opacity:1; transform:translateX(-50%) perspective(700px) rotateX(0deg) translateY(0); }
}
html[data-theme="dark"] .site-nav-menu{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .08),
    0 2px 4px oklch(0% 0 0 / .4),
    0 12px 24px -12px oklch(0% 0 0 / .55),
    0 26px 54px -28px oklch(0% 0 0 / .6);
}
/* Quem pediu menos movimento recebe o menu pronto, sem a tampa girando. A sombra em
   camadas fica: profundidade não é movimento. */
@media (prefers-reduced-motion: reduce){
  .site-nav-menu{ animation:none; }
}
.site-nav-menu a{
  padding:.5rem .7rem; border-radius:var(--r-sm); color:var(--text); text-decoration:none; font-size:.85rem;
  transition:background-color .15s ease, color .15s ease;
}
.site-nav-menu a:hover, .site-nav-menu a:focus-visible{ background:var(--surface-2); color:var(--accent-strong); outline:none; }
.nav-home{ display:inline-flex; align-items:center; gap:.35rem; text-decoration:none; user-select:none; }
.nav-home:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
/* "Você está aqui": marcado com o acento, sem convidar clique. */
/* "Você está aqui" ganha o mesmo afundamento do menu aberto: é um estado permanente de
   tecla pressionada, e isso diz "já estamos nesta" sem precisar de mais uma cor. */
.nav-home[aria-current="page"]{
  color:var(--accent-strong); background:var(--accent-soft); border-color:transparent; cursor:default; font-weight:600;
  box-shadow:inset 0 2px 3px oklch(0% 0 0 / .12);
}
html[data-theme="dark"] .nav-home[aria-current="page"]{
  box-shadow:inset 0 2px 4px oklch(0% 0 0 / .35);
}
/* Entre o botão e o menu existe um vão de .4rem: sem preencher, atravessá-lo
   conta como "saiu do menu" e ele fecharia no caminho (initNavHover). */
.site-nav .col-picker[open] .site-nav-menu::before{
  content:""; position:absolute; left:0; right:0; top:-.45rem; height:.45rem;
}
@media (max-width: 720px){
  .site-nav{ padding:.6rem 1rem; position:relative; justify-content:center; }
  /* Em telas estreitas ancora os 4 menus no centro da BARRA (não de cada
     botão): medido a 375px, "Rankings ▾" e "Personagens ▾" ficavam com left
     negativo. A especificidade extra é necessária pra vencer a regra mobile do
     seletor de colunas (.col-picker-menu{left:0}), que empataria e ganharia por
     ordem de declaração. */
  .site-nav .col-picker{ position:static; }
  .site-nav .site-nav-menu{ left:50%; right:auto; transform:translateX(-50%); }
}

/* ═══ busca global ═══ */
.global-search{ display:flex; align-items:center; position:relative; margin-left:auto; }
.global-search input{
  width:16rem; max-width:100%; padding:.45rem .7rem; border-radius:var(--r-md);
  border:1px solid var(--border); background:var(--surface-2); color:var(--text);
  font-family:inherit; font-size:.82rem; transition:border-color .15s ease, box-shadow .15s ease;
}
.global-search input::placeholder{ color:var(--muted); }
/* É o desenho de uma TECLA de teclado — o caso mais literal de relevo do site inteiro, e
   estava chapado. A camada `0 1px 0` sólida (sem desfoque) é o corpo da tecla visto de
   frente; é ela que separa "tecla" de "retângulo com borda". */
.search-kbd{
  position:absolute; right:.5rem; pointer-events:none; font-family:var(--font-mono);
  font-size:.62rem; font-weight:500; color:var(--muted); background:var(--surface-3);
  border:1px solid var(--border); border-radius:var(--r-sm); padding:.1rem .35rem; line-height:1;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .6),
    0 1px 0 oklch(0% 0 0 / .18),
    0 1px 2px oklch(0% 0 0 / .12);
}
html[data-theme="dark"] .search-kbd{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 0 oklch(0% 0 0 / .5),
    0 2px 3px oklch(0% 0 0 / .35);
}
.global-search input:focus ~ .search-kbd{ display:none; }
/* z-index alto: a lista precisa passar por cima do conteúdo e dos menus da nav. */
.global-search-results{
  position:absolute; top:calc(100% + .3rem); right:0; z-index:60;
  min-width:100%; max-height:17rem; overflow-y:auto;
  background:var(--surface); border:1px solid var(--border-strong); border-radius:var(--r-md);
  box-shadow:var(--shadow-flutuante);
}
.global-search-item{ padding:.45rem .75rem; font-size:.84rem; cursor:pointer; white-space:nowrap; transition:background-color .1s; }
.global-search-item:hover, .global-search-item.active{ background:var(--accent-soft); color:var(--accent-strong); font-weight:600; }
.global-search-empty{ padding:.5rem .7rem; font-size:.8rem; color:var(--muted); }
/* Personagem é o caso comum e fica sem etiqueta: só o que foge do esperado se anuncia. */
.search-tag{
  margin-left:.4rem; font-family:var(--font-mono); font-size:.58rem; letter-spacing:.08em; text-transform:uppercase;
  padding:.05rem .35rem; border-radius:var(--r-pill); background:var(--accent-soft); color:var(--accent);
  font-weight:500; vertical-align:middle;
}
.global-search-item .muted{ margin-left:.4rem; }
.cmp-field{ position:relative; display:inline-block; }

/* ═══ calculadora ═══ */
.calc-layout{ display:flex; justify-content:center; padding:1.6rem 1.8rem; }
.calc-panel{ width:min(560px, 100%); }
.calc-form{ display:flex; flex-direction:column; gap:.4rem; margin-top:.8rem; }
.calc-form label{ font-family:var(--font-mono); font-size:.68rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin-top:.6rem; }
.calc-form input{
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r-md);
  padding:.5rem .7rem; color:var(--text); font-size:.88rem;
}
.calc-grid{ display:grid; grid-template-columns:repeat(3, 1fr); gap:.8rem; margin-top:.3rem; }
/* Sem width:100% o <input> usa a largura INTRÍNSECA (~20 caracteres): os 3
   campos saíam de tamanhos diferentes e o placeholder cortado. .calc-field é o
   invólucro que o autocomplete do shared.js cria. min-width:0 impede que um
   valor comprido estufe a coluna. */
.calc-form input, .calc-field{ width:100%; }
.calc-grid > div{ min-width:0; }
.calc-form button{ align-self:flex-start; margin-top:1rem; padding:.55rem 1.4rem; font-size:.8rem; }
.calc-result{ margin-top:1.3rem; padding-top:1rem; border-top:1px solid var(--border); display:flex; flex-direction:column; gap:.5rem; }
/* display:flex acima sobrepõe o [hidden] do atributo — sem isto o painel de
   resultado (vazio) apareceria já no load. Armadilha registrada: qualquer
   elemento com [hidden] e display explícito precisa da própria regra. */
.calc-result[hidden]{ display:none; }
.calc-result-row{ display:flex; justify-content:space-between; align-items:baseline; }
.calc-result-row strong{ font-family:var(--font-mono); }
.calc-note{ margin-top:1rem; }
@media (max-width: 560px){ .calc-grid{ grid-template-columns:1fr; } }

/* ═══ status do sistema ═══ */
.status-layout{ display:grid; grid-template-columns:repeat(auto-fit, minmax(320px,1fr)); gap:1rem; padding:1.6rem 1.8rem; }
.status-block{ display:flex; flex-direction:column; gap:.6rem; }
.status-row{ display:flex; justify-content:space-between; align-items:center; gap:.6rem; font-size:.86rem; }
.status-row .compare-label{ color:var(--muted); }
/* MEDIDO em 13/08: as 17 linhas desta página estavam com `box-shadow: none` — retângulos
   pintados dentro de um `.panel` que TEM três camadas de sombra. Relevo no contêiner e
   nada dentro: a página que informa a saúde do sistema era a única ainda inteiramente
   plana por dentro. Aqui a linha vira peça pousada, com a mesma linguagem do `.chip`:
   aresta de cima iluminada e sombra de contato embaixo. */
.status-guild-row{
  display:flex; justify-content:space-between; align-items:center; gap:.6rem;
  padding:.5rem .7rem; border-radius:var(--r-md); background:var(--surface-2); font-size:.85rem;
  border:1px solid var(--border);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .16);
}
html[data-theme="dark"] .status-guild-row{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .34);
}
/* Cadência esperada do job, ao lado do "há X min". Discreta: é a régua para ler a idade,
   não um dado em si. E dois selos na mesma linha (Atrasado + OK/Falhou) precisam de ar. */
.job-cadencia{ opacity:.7; }
.status-guild-row .status-pill + .status-pill{ margin-left:.35rem; }
.status-guild-row > span:last-child{ display:inline-flex; align-items:center; gap:.35rem; white-space:nowrap; }

/* ── volume nas linhas de status ──
   O `:has()` prende a perspectiva a este bloco: `.status-block` também empacota linhas de
   texto simples (`.status-row`) em outras seções, e ali não há camada nenhuma a revelar.
   Onde `:has()` não existir, nada acontece — que é o mesmo que hoje. */
.tem-3d .status-block:has(> .status-guild-row){
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .status-guild-row{
  transform-style:preserve-3d;
  will-change:transform;
  transition:transform .25s cubic-bezier(.22,.61,.36,1), box-shadow .18s ease, border-color .18s ease;
}
.tem-3d .status-guild-row:hover{
  border-color:color-mix(in oklch, var(--accent) 38%, var(--border));
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 2px 4px oklch(38% .06 250 / .16),
    0 10px 20px -12px oklch(38% .06 250 / .32);
}
.tem-giro .status-guild-row:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }
html[data-theme="dark"] .tem-3d .status-guild-row:hover{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 2px 5px oklch(0% 0 0 / .4),
    0 12px 24px -14px oklch(0% 0 0 / .58);
}
/* A pastilha vem à frente porque é o veredito; o texto fica logo atrás. Os dois lados são
   `<span>` sem classe, então os seletores são posicionais — é o que o HTML oferece.
   `display:block` no lado do texto pelo motivo medido no ciclo 48: `transform` não se
   aplica a elemento `display:inline`, e este nasce inline. */
.tem-3d .status-guild-row > span:first-child{ display:block; transform:translateZ(8px); }
/* O `<span>` da direita é só um agrupador das pastilhas, mas precisa entrar no 3D: um
   `flat` no meio da cadeia achata tudo abaixo. Terceira vez que esta mesma armadilha
   aparece (`.riv-corpo` no ciclo 45, `.guild-card-corpo` no 46) — MEDIDO aqui: a pastilha
   a Z=16 ficava em 33,5 px, exatamente a largura sem transform; com o `preserve-3d` no
   agrupador, 34,1 px. */
.tem-3d .status-guild-row > span:last-child{ transform-style:preserve-3d; }
.tem-3d .status-guild-row .status-pill{ transform:translateZ(16px); }

/* Mesma correção do item de charm: a linha já estava na lista de alvos do shared.js, que
   escrevia `--luz-x`/`--luz-y` nela a cada movimento do ponteiro sem que nada lesse. */
.tem-3d .status-guild-row{ position:relative; }
.tem-3d .status-guild-row::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .2s ease; z-index:0;
  background:radial-gradient(circle at var(--luz-x, 50%) var(--luz-y, 50%),
    color-mix(in oklch,
      color-mix(in oklch, oklch(100% 0 0) var(--luz-branco, 0%), var(--accent))
      var(--luz-alfa-card, 22%), transparent),
    transparent 60%);
}
.tem-3d .status-guild-row:hover::before{ opacity:1; }
.tem-3d .status-guild-row > *{ position:relative; z-index:1; }

@media (prefers-reduced-motion: reduce){
  /* Só o giro sai; as camadas em translateZ ficam (ver o bloco do .summary, linha ~476). */
  .tem-3d .status-guild-row, .tem-3d .status-guild-row:hover{ transform:none; transition:none; }
}

/* A pastilha é o veredito da linha — é ela que diz OK ou falhou, e era plana. Ganha o
   relevo de um selo aplicado por cima: fio de luz na aresta de cima e sombra de contato
   embaixo. Não leva `inset` de peça encaixada (como a medalha do pódio ou o brasão da
   guilda) de propósito: aqueles são cunhados NA superfície, este é colado SOBRE ela, e é
   essa diferença que faz o olho achá-lo primeiro numa lista de 17 linhas iguais. */
.status-pill{
  display:inline-flex; align-items:center; gap:.35rem; padding:.15rem .55rem; border-radius:var(--r-pill);
  font-family:var(--font-mono); font-size:.66rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  border:1px solid transparent;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .status-pill{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .1),
    0 1px 2px oklch(0% 0 0 / .4);
}
.status-pill.ok{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 30%, transparent); }
.status-pill.bad{ background:var(--offline-soft); color:var(--offline); border-color:color-mix(in oklch, var(--offline) 30%, transparent); }
/* 'warn' = em andamento (ex.: cobertura do ranking subindo). Não é erro como o
   'bad', nem pronto como o 'ok'. */
.status-pill.warn{ background:var(--warn-soft); color:var(--warn); border-color:color-mix(in oklch, var(--warn) 30%, transparent); }

table.hide-world .col-world,
table.hide-rank .col-rank,
table.hide-vocation .col-vocation,
table.hide-gained .col-gained,
table.hide-online-time .col-online-time,
table.hide-priority .col-priority,
table.hide-boss .col-boss,
table.hide-drome .col-drome,
table.hide-achievement .col-achievement,
table.hide-prestige .col-prestige,
table.hide-bounty .col-bounty{ display:none; }

/* ═══ painéis de insight ═══ */
.insights{
  padding:0 1.8rem 1.3rem; display:grid; grid-template-columns:repeat(auto-fit, minmax(340px,1fr)); gap:1rem;
  align-items:stretch;
}
.panel{ padding:1.25rem 1.35rem; }
.panel-header{ display:flex; align-items:baseline; justify-content:space-between; gap:.6rem; flex-wrap:wrap; margin-bottom:.85rem; }
.panel-header h2{
  margin:0; font-size:.92rem; display:flex; align-items:center; gap:.5rem;
  color:var(--text); text-transform:uppercase; letter-spacing:.11em; font-weight:600;
}
.panel-header a{ font-size:.78rem; text-decoration:none; }
.panel-header a:hover{ text-decoration:underline; }
.small{ font-size:.74rem; }

/* Botão secundário. Nasceu como #export-csv e as páginas novas criaram ids
   próprios (rk-export, ed-export, gr-export, gp-export) que ficaram SEM estilo.
   A classe existe pra que o próximo não repita isso: basta class="btn-ghost". */
#export-csv, .btn-ghost{
  border:1px solid var(--border); background:var(--surface-2);
  color:var(--muted); border-radius:var(--r-md); padding:.4rem .9rem; font-size:.7rem; cursor:pointer;
  font-family:var(--font-display); font-weight:600; letter-spacing:.1em; text-transform:uppercase;
  transition:border-color .15s ease, color .15s ease;
}
#export-csv:hover, .btn-ghost:hover{ border-color:var(--accent); color:var(--accent-strong); }
.btn-ghost:disabled{ opacity:.5; cursor:default; border-color:var(--border); }

/* Mesmo relevo de tecla do botão primário, em tom mais discreto — este é o botão
   secundário e não deve competir com o principal pela atenção. */
/* MEDIDO em 13/08 comparando a energia da sombra PROJETADA nos dois temas: este botão
   ficava em 0,10 no claro e 0,10 no escuro, enquanto os vizinhos da mesma barra
   (`.theme-toggle`, `.auth-button`) vão de 0,20 para 0,40. Fundo escuro ABSORVE sombra —
   manter o mesmo alfa não é "manter o efeito", é apagá-lo.

   O valor mora em `--sombra-btn-2rio` (topo do arquivo) para valer nos DOIS caminhos do
   tema escuro. Segue discreto: reforça na mesma proporção dos vizinhos (2x), partindo de
   um valor mais baixo, porque não deve competir com o botão principal. */
#export-csv, .btn-ghost{
  box-shadow:var(--sombra-btn-2rio);
  transition:border-color .15s ease, color .15s ease, box-shadow .12s ease, transform .12s ease;
}
#export-csv:active, .btn-ghost:active:not(:disabled){
  transform:translateY(1px);
  box-shadow:inset 0 2px 3px oklch(0% 0 0 / .22);
}
/* Desabilitado não afunda nem levanta: não há nada para apertar. */
.btn-ghost:disabled{ box-shadow:none; transform:none; }

/* Quem pediu menos movimento continua com o relevo (é estático e informa a affordance),
   mas sem o pulinho ao clicar. */
@media (prefers-reduced-motion: reduce){
  #watchlist-form button:active, #alertas-form button:active, #login-submit:active,
  .calc-form button:active, .hero-search button:active,
  #export-csv:active, .btn-ghost:active:not(:disabled){ transform:none; }
}

.heatmap-guild{ margin-bottom:1rem; position:relative; z-index:1; }
.heatmap-guild:last-child{ margin-bottom:0; }
.heatmap-guild .g-label{ font-family:var(--font-mono); font-size:.68rem; color:var(--accent-strong); margin-bottom:.35rem; letter-spacing:.08em; text-transform:uppercase; }
.weekday-heading{ margin-top:.8rem; color:var(--muted) !important; font-family:var(--font-mono) !important; font-weight:500 !important; }
.weekday-bars{ height:2.4rem; }
.weekday-bars .heatmap-bar{ flex:0 1 2.2rem; }
.heatmap-bars{
  display:flex; align-items:flex-end; gap:2px; height:3.2rem; padding:3px 3px 0;
  background:color-mix(in oklch, var(--surface-3) 55%, transparent);
  border:1px solid var(--border); border-radius:var(--r-md);
}
.heatmap-bar{ flex:1; background:var(--surface-3); border-radius:var(--r-xs) var(--r-xs) 0 0; position:relative; min-height:2px; }
.heatmap-bar.has-data{ background:linear-gradient(180deg, var(--gold-hi), var(--accent)); }
.heatmap-bar:hover::after{
  content:attr(data-label); position:absolute; bottom:100%; left:50%; transform:translateX(-50%);
  background:var(--text); color:var(--bg); font-family:var(--font-mono); font-size:.66rem; padding:.15rem .4rem; border-radius:var(--r-sm);
  white-space:nowrap; margin-bottom:.3rem; pointer-events:none; z-index:6;
}
.heatmap-labels{ display:flex; justify-content:space-between; font-family:var(--font-mono); font-size:.64rem; color:var(--muted); margin-top:.25rem; }
.vulnerability{ font-size:.78rem; color:var(--text); margin-top:.5rem; }
.vulnerability b{ color:var(--accent-strong); }

/* Altura fixa: rola internamente quando tem mais eventos do que cabe, sem
   sobrar vão vazio quando tem menos. */
.events-list{ list-style:none; margin:0; padding:0; height:29rem; overflow-y:auto; position:relative; z-index:1; }
.events-list li{ font-size:.82rem; padding:.45rem 0 .45rem 1rem; border-bottom:1px solid var(--border); position:relative; }
.events-list li::before{
  content:""; position:absolute; left:0; top:.85rem; width:5px; height:5px;
  background:var(--accent); border-radius:var(--r-xs);
}
.events-list li:last-child{ border-bottom:none; }
.events-list .ev-time{ color:var(--muted); font-family:var(--font-mono); font-size:.68rem; display:block; }
.events-empty{ color:var(--muted); font-size:.82rem; }

/* ═══ tabela — o coração do site ═══ */
.table-wrap{ padding:0 1.8rem 3rem; overflow-x:auto; }
/* O `overflow-x:auto` acima existe para a tabela (min-width:800px) poder ser arrastada
   no celular. O efeito colateral MEDIDO: `overflow-y` computa como 'auto' junto, o que
   faz do .table-wrap o scrollport de referência do sticky do thead — e ele tem 8.277 px
   de altura própria, então nunca rola e o cabeçalho nunca gruda.
   Devolver `visible` traz o sticky de volta, mas SÓ pode acontecer onde a tabela cabe.
   MEDIDO ao tentar por media query (min-width:900px): rankings/exp-daily/deaths/
   guild-ranking couberam com folga, mas o DASHBOARD tem tabela de ~2.000 px e vazou
   1.163 px, empurrando 1.120 px de rolagem horizontal na página. Largura de tela não
   responde à pergunta certa; quem responde é a tabela. Por isso a classe vem do JS
   (marcarTabelasQueCabem), que mede — e o padrão sem JS continua sendo o seguro. */
.table-wrap.cabe{ overflow-x:visible; }
/* SEM a classe `cabe`, o cabeçalho NÃO gruda. Esta é a outra metade do problema descrito
   logo acima, e a que aparecia na tela: quando o wrap fica em `overflow:auto` (celular, ou
   qualquer tabela larga demais para a caixa), ele vira o scrollport do sticky — e aí o
   `top: var(--h-topbar)` deixa de contar a partir do alto da JANELA e passa a contar do
   alto da TABELA. O cabeçalho descia ~78 px para dentro dela e ficava parado no meio das
   linhas, por cima do primeiro colocado, com o resto do ranking passando por baixo.
   MEDIDO em 390 px: "1 Hard Paradox", depois a faixa POSIÇÃO/NOME/LEVEL cobrindo o
   segundo, depois "3 Bacon Almighty".
   Grudar dentro de um scrollport que não rola na vertical não serve para nada — o valor
   do sticky é acompanhar a rolagem da página, e é justamente isso que ali não acontece.
   Com `static` o cabeçalho volta a ser a primeira linha da tabela e rola junto, que é o
   comportamento certo quando a tabela é arrastada de lado. Onde a tabela cabe, nada muda:
   `.cabe` devolve `overflow:visible` e o sticky segue funcionando como sempre. */
.table-wrap:not(.cabe) thead th{ position:static; }
/* A borda que come o conteúdo: onde a tabela é maior que a caixa, ela desvanece em vez de
   ser decepada, e o olho lê que a coluna continua por baixo da margem. Quem liga a classe
   e mede os dois lados é `iniciarSombraDeRolagem` — os padrões abaixo são o estado "no
   começo da rolagem", então mesmo se o JS parar depois de ligar a classe, o resultado é
   coerente (nada à esquerda, algo à direita).
   `mask` é grouping property e força `transform-style: flat` nos descendentes: aqui não
   custa nada, a tabela já era `flat` (medido antes de aplicar). */
.table-wrap.tem-corte{
  --corte-esq:0px;
  --corte-dir:26px;
  -webkit-mask-image:linear-gradient(to right,
    transparent 0, #000 var(--corte-esq),
    #000 calc(100% - var(--corte-dir)), transparent 100%);
  mask-image:linear-gradient(to right,
    transparent 0, #000 var(--corte-esq),
    #000 calc(100% - var(--corte-dir)), transparent 100%);
}
table{
  width:100%; border-collapse:separate; border-spacing:0; font-size:.85rem; min-width:800px;
  background:var(--card-gradient); border:1px solid var(--border); border-radius:var(--r-lg);
  box-shadow:var(--shadow-card);
  /* SEM overflow:hidden aqui — mata o position:sticky do thead th. */
}
thead th{
  text-align:left; font-family:var(--font-mono); font-size:.64rem; text-transform:uppercase; letter-spacing:.12em;
  color:var(--muted); font-weight:500;
  border-bottom:1px solid var(--border-strong); padding:.8rem .8rem;
  /* Gruda logo ABAIXO da topbar (que tem 78,4 px: 56 da logo + .7rem de padding em
     cima e embaixo) — senão o cabeçalho da tabela sobe para trás dela e some.
     A topbar tem z-index 10 contra os 2 daqui, então ela continua por cima. */
  position:sticky; top:var(--h-topbar); z-index:2; background:var(--thead-grad);
  white-space:nowrap;
  /* A faixa de cabeçalho é uma peça ACIMA do corpo da tabela — com 200 linhas passando
     por baixo dela, essa é a camada mais visível do site. Sombra projetada para baixo
     (as linhas somem sob a peça) e um fio de luz na aresta de cima.

     ESTE é o estado pousado: o cabeçalho ainda está encostado no topo da própria tabela e
     não há linha nenhuma por baixo dele. Fica só o fio de luz e um contato mínimo, pela
     mesma razão que a `.topbar` não tem sombra no topo da página — sombra sem nada para
     sombrear é decoração afirmando uma profundidade que não existe. A elevação de verdade
     entra na regra `.cabecalho-grudado` abaixo. */
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px -1px oklch(0% 0 0 / .12);
  transition:box-shadow .2s ease;
}
html[data-theme="dark"] thead th{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 2px -1px oklch(0% 0 0 / .3);
}
/* ── grudado: agora existe conteúdo correndo por baixo ──
   A classe vem do JS (iniciarCabecalhoFlutuante), que compara a posição do `thead` com a
   do topo da tabela: enquanto estão juntos, o cabeçalho está pousado; quando o `thead` se
   descola, o `sticky` assumiu e as linhas passaram para baixo dele. Sem JS, o cabeçalho
   fica no estado pousado — discreto, nunca quebrado. */
table.cabecalho-grudado thead th{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 2px 3px -1px oklch(0% 0 0 / .16),
    0 6px 12px -8px oklch(38% .06 250 / .4);
}
html[data-theme="dark"] table.cabecalho-grudado thead th{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 2px 4px -1px oklch(0% 0 0 / .4),
    0 8px 16px -8px oklch(0% 0 0 / .5);
}
/* A sombra maior é profundidade, não animação, e por isso vale para todo mundo. O que sai
   para quem pediu menos movimento é só a transição entre os dois estados. */
@media (prefers-reduced-motion: reduce){
  thead th{ transition:none; }
}
th.num, td.num{ text-align:right; }
/* Coluna pela qual o ranking está ordenado: um pouco mais de peso ajuda o olho
   a fixá-la sem competir por cor com o Exp Diário (verde). */
td.rk-value{ font-weight:600; color:var(--text); }
/* Mundo e Vocação aparecem duas vezes na linha do ranking: como colunas próprias e,
   repetidos, num span embaixo do nome. O span é o plano B do celular, onde as duas
   colunas somem para o VALOR caber na tela — mas a regra que fazia isso NUNCA foi
   escrita, então ele estava aceso no desktop também e cada linha repetia
   "Solarian · Royal Paladin" ao lado do nome, antes das colunas de verdade.
   Escondido por padrão; só a media query abaixo o acende. */
.rk-so-mobile{ display:none; }
/* Legenda que precede os resultados vindos da busca no ranking INTEIRO: eles estão fora
   das 200 posições carregadas, e sem o aviso o rank saltando de 200 para 757 é lido como
   erro de ordenação. Também estava sem regra nenhuma — a linha herdava a zebra e o hover
   de acento das linhas de dado, ou seja, vestia-se de resultado do ranking quando é
   legenda do que vem abaixo. O fundo vai no td porque a zebra e o hover pintam o tr. */
.rk-linha-aviso td{
  color:var(--muted); font-size:.76rem; line-height:1.45; padding:.5rem .8rem;
  background:color-mix(in oklch, var(--accent-soft) 55%, var(--surface-2));
  border-bottom:1px solid var(--border-strong);
}
/* Sem realce de linha clicável: não há personagem aqui para o olho perseguir. */
.rk-linha-aviso:hover{ background:none; box-shadow:none; }
.rank-plain{ color:var(--muted); }
/* Pódio do top 3. A tinta é ESCURA e fixa sobre disco claro: é o que garante
   contraste nos dois temas — número claro sobre disco metálico reprova no tema
   claro (a mesma restrição que o arquivo antigo já registrava aqui). */
/* ── medalhas do pódio ──
   MEDIDO em 10/08: eram um gradiente com um contorno de 1 px — chapadas. Medalha é um
   objeto de metal, e metal se lê pelo BISEL: luz na aresta de cima, sombra na de baixo,
   e uma sombra projetada dizendo que a peça está acima da linha da tabela.

   Aqui o relevo é assumido pelo box-shadow (e não por transform) porque a peça tem 18 px:
   nesse tamanho um giro é imperceptível e ainda borraria o número dentro dela. */
.rank-medal{
  display:inline-flex; align-items:center; justify-content:center;
  min-width:1.7em; height:1.7em; padding:0 .35em; border-radius:var(--r-sm);
  font-weight:700; font-size:.78em; color:oklch(22% .03 250);
  position:relative; isolation:isolate;
  /* A ALTURA DO DEGRAU vive nesta variável, e não solta no box-shadow, porque as outras
     quatro camadas valem para as três medalhas: redeclarar `box-shadow` na regra da
     posição apagaria todas elas (foi assim que perdi o relevo das barras no ciclo 64). */
  --elev-medalha: 0 1px 2px oklch(0% 0 0 / .3);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),        /* aresta de cima pegando luz */
    inset 0 -1px 0 oklch(0% 0 0 / .28),        /* aresta de baixo na sombra */
    inset 0 0 0 1px oklch(0% 0 0 / .14),       /* contorno que já existia */
    var(--elev-medalha),                       /* a peça projeta sombra na linha */
    0 0 0 .5px oklch(0% 0 0 / .06);
  /* O texto ganha um leve relevo gravado, como número cunhado na peça. */
  text-shadow:0 1px 0 oklch(100% 0 0 / .35);
}
/* Brilho especular: a faixa clara que corre no alto de uma superfície curva. Fica atrás
   do número (z-index) e não intercepta clique. */
.rank-medal::before{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  z-index:-1;
  background:linear-gradient(160deg, oklch(100% 0 0 / .42) 0%, oklch(100% 0 0 / .08) 38%, transparent 62%);
}
.rank-medal.rank-1{ background:linear-gradient(145deg, oklch(89% .1 195), oklch(77% .13 205)); }
.rank-medal.rank-2{ background:linear-gradient(145deg, oklch(91% .012 250), oklch(79% .018 250)); }
.rank-medal.rank-3{ background:linear-gradient(145deg, oklch(85% .08 62), oklch(72% .1 52)); }

/* ── o pódio tem três alturas ──
   MEDIDO em 13/08 na tela: a LINHA da tabela já escalonava por posição (inset de topo
   .55 / .42 / .3), mas as três medalhas projetavam sombra idêntica — `0 1px 2px / .3`
   nas três. O sistema afirmava "o primeiro está mais alto" pela linha e desmentia na
   peça que é a própria metáfora de altura.
   O degrau sobe junto no deslocamento e no borrão (1→2→3 px), que é o que o olho lê como
   distância até a mesa; o alfa acompanha na proporção que a linha já usava. */
.rank-medal.rank-2{ --elev-medalha: 0 2px 4px oklch(0% 0 0 / .38); }
.rank-medal.rank-1{ --elev-medalha: 0 3px 6px oklch(0% 0 0 / .48); }

/* No escuro a sombra projetada precisa ser mais funda para a peça não sumir no fundo. */
html[data-theme="dark"] .rank-medal{
  --elev-medalha: 0 2px 4px oklch(0% 0 0 / .55);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    inset 0 -1px 0 oklch(0% 0 0 / .38),
    inset 0 0 0 1px oklch(0% 0 0 / .2),
    var(--elev-medalha),
    0 0 0 .5px oklch(0% 0 0 / .1);
}
/* O par escuro precisa ser explícito: `html[data-theme="dark"] .rank-medal` (0,2,1) vence
   `.rank-medal.rank-1` (0,2,0), então sem estas duas o escuro achataria o pódio de novo. */
html[data-theme="dark"] .rank-medal.rank-2{ --elev-medalha: 0 3px 7px oklch(0% 0 0 / .68); }
html[data-theme="dark"] .rank-medal.rank-1{ --elev-medalha: 0 4px 9px oklch(0% 0 0 / .82); }
/* ── a coluna ordenada fica PRESSIONADA ──
   MEDIDO em 12/08: as 6 colunas ordenáveis são teclas (cursor pointer, tabindex, aria-sort)
   e a coluna ativa só mudava de COR — todas as 7 com o mesmo `box-shadow`. Esta é a última
   família do site onde "ligado" não tinha leitura física, e o vocabulário já existe:
   menu aberto afunda, "você está aqui" afunda, tier escolhido afunda, filtro ligado afunda.
   As duas camadas PROJETADAS do `thead th` são repetidas em todos os estados: elas é que
   fazem a faixa do cabeçalho pairar sobre as 200 linhas, e perdê-las ao ordenar seria
   trocar um ganho por uma perda maior. */
th.sortable{ cursor:pointer; user-select:none; }
th.sortable:hover{
  color:var(--text);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .7),
    0 2px 3px -1px oklch(0% 0 0 / .16),
    0 6px 12px -8px oklch(38% .06 250 / .4);
}
/* Depois do `:hover` de propósito: as duas empatam em especificidade e, ao apontar a
   coluna JÁ ordenada, ela deve continuar afundada em vez de subir. */
th.sortable.active{
  color:var(--accent-strong);
  box-shadow:
    inset 0 2px 3px oklch(0% 0 0 / .18),
    0 2px 3px -1px oklch(0% 0 0 / .16),
    0 6px 12px -8px oklch(38% .06 250 / .4);
}
/* O afundamento precisa ser mais fundo no escuro: a sombra interna compete com um fundo
   que já é escuro. O `:hover` desta célula já tinha par; era só o `:active` que faltava. */
th.sortable:active{ box-shadow:var(--sombra-th-active); }
html[data-theme="dark"] th.sortable:hover{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .16),
    0 2px 4px -1px oklch(0% 0 0 / .4),
    0 8px 16px -8px oklch(0% 0 0 / .5);
}
html[data-theme="dark"] th.sortable.active{
  box-shadow:
    inset 0 2px 4px oklch(0% 0 0 / .45),
    0 2px 4px -1px oklch(0% 0 0 / .4),
    0 8px 16px -8px oklch(0% 0 0 / .5);
}
.sort-arrow{ display:inline-block; width:.7em; }
tbody td{ padding:.68rem .8rem; border-bottom:1px solid color-mix(in oklch, var(--border) 55%, transparent); }
tbody tr:nth-child(even){ background:color-mix(in oklch, var(--surface-3) 26%, transparent); }
tbody tr{ transition:background-color .1s ease, box-shadow .1s ease; }
tbody tr:hover{ background:color-mix(in oklch, var(--accent-soft) 70%, transparent); box-shadow:inset 2px 0 0 var(--accent); }

/* ── a linha sob o cursor sai do plano da tabela ──
   DUAS MEDIÇÕES decidiram a forma disto, em 10/08:
     • `transform` na <tr> **desalinha as colunas** (medido: a 1ª célula pulou de 30 px
       para 24 px). Descartado — tabela não aceita ser empurrada.
     • `box-shadow` EXTERNO na <tr> não é pintado: fica zerado mesmo depois da transição.
       Nas CÉLULAS funciona. Então o relevo mora no <td>.
   O resultado é uma barra que ganha aresta iluminada em cima e sombra embaixo, como se
   tivesse subido um degrau — sem um pixel de deslocamento, que numa tabela de 200 linhas
   seria enjoativo e ainda quebraria o alinhamento. */
/* ── o pódio tem altura ──
   MEDIDO em 12/08: as 3 primeiras linhas de um ranking de 200 só se distinguem pela
   medalha (que ganhou relevo no ciclo 6). A LINHA em si é igual às outras 197 — mesmo
   fundo zebrado, sombra nenhuma. Pódio é, literalmente, um degrau de três alturas: aqui
   o 1º recebe a luz mais forte e a sombra mais funda, e a intensidade cai até o 3º.
   A hierarquia passa a ser lida antes de qualquer número.

   `:has()` e não `:nth-child(-n+3)` de propósito: o relevo tem que seguir quem TEM
   medalha, não as três primeiras linhas de qualquer tabela do site (bazaar-stats,
   por exemplo, não é ranking). Onde `:has()` não existir, degrada para o de sempre.
   Fica ANTES do `:hover` porque as duas regras empatam em especificidade (0,1,3) e o
   ponteiro precisa vencer o pódio. */
tbody tr:has(.rank-1) > td{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .55),
    inset 0 -2px 2px oklch(0% 0 0 / .16);
}
tbody tr:has(.rank-2) > td{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .42),
    inset 0 -1px 1px oklch(0% 0 0 / .12);
}
tbody tr:has(.rank-3) > td{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .3),
    inset 0 -1px 0 oklch(0% 0 0 / .1);
}
/* No escuro a luz branca do claro vira um risco: os alfas caem e a sombra compensa. */
html[data-theme="dark"] tbody tr:has(.rank-1) > td{
  box-shadow:inset 0 1px 0 oklch(100% 0 0 / .14), inset 0 -2px 3px oklch(0% 0 0 / .34);
}
html[data-theme="dark"] tbody tr:has(.rank-2) > td{
  box-shadow:inset 0 1px 0 oklch(100% 0 0 / .1), inset 0 -1px 2px oklch(0% 0 0 / .26);
}
html[data-theme="dark"] tbody tr:has(.rank-3) > td{
  box-shadow:inset 0 1px 0 oklch(100% 0 0 / .07), inset 0 -1px 0 oklch(0% 0 0 / .2);
}
tbody tr:hover > td{
  box-shadow:
    inset 0 1px 0 color-mix(in oklch, var(--accent) 30%, transparent),
    inset 0 -1px 0 oklch(0% 0 0 / .22);
}
/* As linhas já coloridas (ganhando/perdendo exp) têm significado próprio: o relevo usa a
   cor DELAS, senão o ciano do acento apagaria o verde/âmbar que informa a tendência. */
tbody tr.row-gaining:hover > td{
  box-shadow:inset 0 1px 0 color-mix(in oklch, var(--online) 34%, transparent), inset 0 -1px 0 oklch(0% 0 0 / .22);
}
tbody tr.row-slowing:hover > td{
  box-shadow:inset 0 1px 0 color-mix(in oklch, var(--warn) 34%, transparent), inset 0 -1px 0 oklch(0% 0 0 / .22);
}
/* A transição já existente na linha (.1s) cobre o fundo; a das células precisa da própria,
   senão o relevo aparece de estalo enquanto a cor entra suave. */
tbody tr > td{ transition:box-shadow .1s ease; }

@media (prefers-reduced-motion: reduce){
  tbody tr > td{ transition:none; }
}
tbody tr.row-gaining{ background:var(--online-soft); box-shadow:inset 2px 0 0 var(--online); }
tbody tr.row-gaining:hover{ background:var(--online-soft); box-shadow:inset 2px 0 0 var(--online); filter:brightness(1.06); }
tbody tr.row-slowing{ background:var(--warn-soft); box-shadow:inset 2px 0 0 var(--warn); }
tbody tr.row-slowing:hover{ background:var(--warn-soft); box-shadow:inset 2px 0 0 var(--warn); filter:brightness(1.06); }
.gain-badge{
  display:inline-flex; align-items:center; gap:.2rem; color:var(--online);
  font-family:var(--font-mono); font-weight:700; font-size:.72rem; margin-left:.35rem;
}
.gain-badge.slowing{ color:var(--warn); }
.col-status{ width:1.6rem; }
.status-dot{ width:.55rem; height:.55rem; border-radius:50%; display:inline-block; }
.status-dot.online{ background:var(--online); box-shadow:0 0 0 3px var(--online-soft), 0 0 8px var(--online); }
.status-dot.offline{ background:var(--offline); opacity:.45; }
/* Estado ainda não medido (linha instantânea de guilda recém-selecionada): anel vazio,
   nem verde nem cinza — não afirmar online/offline que ninguém mediu. */
.status-dot.unknown{ background:transparent; border:2px dashed var(--offline); opacity:.6; }
.name-link{ color:var(--text); text-decoration:none; font-weight:600; }
.name-link:hover{ color:var(--accent-strong); text-decoration:underline; }
/* ── relevo das pastilhas (o maior conjunto chapado que sobrou) ──
   MEDIDO em 12/08 varrendo 12 páginas atrás de tudo que tem cara de peça (fundo ou borda
   própria) e está sem sombra nenhuma: `.pill` 251, `.voc-pill` 197 e `.guild-tag` 119 —
   **567 pastilhas** cravadas nas tabelas e nas fichas, todas planas, enquanto chip,
   medalha, aba e botão ao redor já tinham volume.
   Os valores são deliberadamente menores que os do chip: a peça tem ~19 px de altura e
   1 px de sombra aqui pesa o mesmo que 2 px num cartão. */
.pill, .voc-pill, .guild-tag{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .pill,
html[data-theme="dark"] .voc-pill,
html[data-theme="dark"] .guild-tag{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 2px oklch(0% 0 0 / .35);
}
.pill{
  display:inline-block; font-family:var(--font-mono); font-size:.68rem; padding:.12rem .5rem; border-radius:var(--r-pill);
  background:var(--surface-2); color:var(--muted); border:1px solid var(--border);
}

/* Barra de progresso das varreduras. Cor de acento (não semântica): estar no
   meio de uma volta é o estado NORMAL. */
.scan-progress-row{ margin-bottom:.9rem; }
.scan-progress-row:last-child{ margin-bottom:0; }
/* Relevo na regra agrupada das barras (a que começa em `.ha-barra`). */
.scan-bar{ height:5px; border-radius:var(--r-pill); background:var(--surface-3); overflow:hidden; margin:.35rem 0 .2rem; }
.scan-bar-fill{ display:block; height:100%; background:var(--bar-grad); transition:width .4s ease; }

.guild-stats{ font-family:var(--font-mono); font-size:.72rem; color:var(--accent); }

/* Rankings no celular: Mundo (3ª coluna) e Vocação (4ª) saem, e o par volta embaixo do
   nome pelo .rk-so-mobile. É a troca que o span sempre quis fazer e nunca pôde, porque
   esta regra não existia — o span só duplicava as colunas no desktop.
   O que ela compra, MEDIDO a 375px: a coluna VALOR — o número pelo qual o ranking está
   ordenado, a razão da página — começava no pixel 556, fora da tela, com 457px de
   rolagem lateral; passa a começar no 340, dentro da tela, com 241px de rolagem. Não é
   "cabe inteiro": o número ainda termina fora e ainda se rola um pouco. Fazer caber de
   verdade exigiria apertar fonte e padding da tabela (testado: chega a 507px de largura,
   ainda não cabe) ou cortar mais uma coluna — outra decisão, não esta.
   O min-width:0 é o que faz a troca valer: sem ele as colunas somem e a tabela continua
   esticada nos 800px do padrão, com o VALOR no mesmo lugar de antes.
   As linhas de estado vazio não são atingidas: elas têm um <td colspan="7"> só, que é
   sempre :nth-child(1). */
@media (max-width:720px){
  #rk-table{ min-width:0; }
  #rk-table th:nth-child(3), #rk-table td:nth-child(3),
  #rk-table th:nth-child(4), #rk-table td:nth-child(4){ display:none; }
  .rk-so-mobile{ display:block; margin-top:.2rem; font-size:.72rem; color:var(--muted); }
  .rk-so-mobile a{ color:var(--muted); }
}

/* ═══ home ═══ */
.hero{ padding:3.2rem 1.8rem 1rem; position:relative; }
.hero-title{
  font-family:var(--font-display); font-size:clamp(2.2rem, 4.6vw, 3.7rem); font-weight:700;
  line-height:1.02; margin:0 0 .7rem; letter-spacing:-.015em;
  /* background-IMAGE, não o atalho `background`: o atalho reseta o
     background-clip pro valor inicial e o título deixaria de ser texto
     recortado, virando uma barra sólida. O gradiente por tema existe porque a
     versão clara precisa vir do lado ESCURO do acento pra passar no contraste. */
  background-image:var(--hero-grad);
  -webkit-background-clip:text; background-clip:text; color:transparent;
  /* MEDIDO em 12/08: o título de cada página tem ~59 px e peso 700 — é o maior elemento
     tipográfico do site — e estava com `text-shadow: none`, letras chapadas coladas no
     papel. Aqui elas se levantam: uma sombra curta de contato e uma difusa de projeção.

     É `filter: drop-shadow` e NÃO `text-shadow` por causa do `background-clip:text` logo
     acima: com o fill transparente, a sombra de texto depende de o motor pintá-la atrás
     de um glifo que tecnicamente não tem cor, o que varia entre navegadores. O
     drop-shadow age sobre o que foi REALMENTE rasterizado — o gradiente recortado nas
     letras — e por isso acompanha a forma delas em qualquer motor.

     A medida vai em `em`, não em px: o título é `clamp(2.2rem, 4.6vw, 3.7rem)` e MEDIDO
     a 375 px ele cai de 58,9 px para 35,2 px. Com 3 px fixos a sombra saltava de 5,1%
     para 8,5% da altura da letra — proporcionalmente 67% mais pesada justo no tamanho
     em que ela mais borra. Em `em` a proporção fica a mesma em qualquer tela, sem
     precisar de media query. */
  filter:
    drop-shadow(0 .017em 0 oklch(100% 0 0 / .5))
    drop-shadow(0 .034em .051em oklch(38% .06 250 / .35));
}
/* No escuro o fio de luz de cima seria um halo branco em volta da letra: some com ele e
   a sombra desce mais, porque o contraste contra fundo escuro pede mais profundidade. */
html[data-theme="dark"] .hero-title{
  filter:drop-shadow(0 .051em .085em oklch(0% 0 0 / .55));
}
/* O EMOJI DO TÍTULO FICA FORA DO RECORTE.
   `background-clip:text` + `color:transparent` acima recortam o gradiente na forma de
   TUDO que é texto — e um emoji é texto. Resultado: o 🏆 do Rankings, o ⚔️ do Comparar e
   os outros 17 saíam pintados de ciano chapado, sem a cor própria deles. Não era o emoji
   "não carregando": ele estava lá, vestido com o gradiente do título.
   `-webkit-text-fill-color` é o que decide isso (é ele que `color:transparent` acaba
   definindo); devolvê-lo ao valor inicial faz o glifo voltar a se pintar sozinho. O
   gradiente segue recortado atrás dele, e o emoji, opaco, o cobre. */
.hero-title .hero-emoji{
  -webkit-text-fill-color:initial;
  color:initial;
  /* O drop-shadow do título é do elemento inteiro e cairia sobre o emoji também,
     engrossando-o. Aqui ele não faz falta: o emoji já tem forma e cor próprias. */
  filter:none;
}
.hero-sub{ color:var(--muted); margin:0; max-width:58ch; font-size:1.02rem; line-height:1.6; }

/* Badge de coleta. Nasce neutro; só pulsa quando home.js confirma coleta
   recente — um ponto verde piscando sobre dado velho seria afirmação falsa. */
.live-badge{
  display:inline-flex; align-items:center; gap:.5rem; margin-bottom:.9rem;
  padding:.28rem .75rem .28rem .6rem; border-radius:var(--r-pill);
  border:1px solid var(--border); background:var(--surface-2);
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted);
}
.live-badge[hidden]{ display:none; }
.live-dot{ width:.45rem; height:.45rem; border-radius:50%; background:var(--muted); flex:0 0 auto; }
.live-badge.live-on{ color:var(--online); border-color:color-mix(in oklch, var(--online) 45%, transparent); }
.live-badge.live-on .live-dot{ background:var(--online); animation:live-pulse 2s ease-in-out infinite; }
@keyframes live-pulse{ 0%,100%{ box-shadow:0 0 0 0 var(--online-soft); } 50%{ box-shadow:0 0 0 5px transparent; } }
@media (prefers-reduced-motion: reduce){
  .live-badge.live-on .live-dot{ animation:none; box-shadow:0 0 0 3px var(--online-soft); }
}

/* Busca em destaque. position:relative porque é o container do autocomplete. */
.hero-search{ position:relative; display:flex; gap:.5rem; margin:1.4rem 0 0; max-width:34rem; }
.hero-search input{
  flex:1 1 auto; min-width:0; padding:.72rem .95rem; border-radius:var(--r-md);
  border:1px solid var(--border-strong); background:var(--surface-2); color:var(--text);
  font-size:.95rem; font-family:var(--font-body);
}
.hero-search button{ flex:0 0 auto; padding:.72rem 1.4rem; font-size:.78rem; }
/* min() e não 22rem fixo: a 375px o campo tem ~317px e uma lista de 352px
   estouraria a lateral só ao abrir — overflow que passa despercebido numa
   conferência com a lista fechada. */
.hero-search .global-search-results{
  top:calc(100% + .35rem); left:0; right:auto; min-width:min(22rem, 100%); max-width:100%;
}

.stats-strip{ display:flex; flex-wrap:wrap; gap:2.2rem; margin-top:1.6rem; }
.stats-strip[hidden]{ display:none; }
.stat-item{ display:flex; flex-direction:column; gap:.15rem; position:relative; padding-left:.85rem; }
.stat-item::before{
  content:""; position:absolute; left:0; top:.15rem; bottom:.15rem; width:2px;
  background:linear-gradient(180deg, var(--accent), transparent);
}
.stat-value{ font-size:1.45rem; font-weight:700; color:var(--text); line-height:1.1; letter-spacing:-.02em; }
.stat-label{ font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.14em; color:var(--muted); }
/* Idade do dado, abaixo do rótulo. Só "Online agora" usa — é o único KPI da home que muda
   por minuto e que a resposta cacheada pode envelhecer. Sem caixa alta nem espaçamento de
   rótulo: é uma ressalva, não um título, e não deve competir com o número. */
.stat-nota{ display:block; font-size:.6rem; color:var(--muted); opacity:.75; margin-top:.15rem; }

.home-cards{
  display:grid; gap:.8rem; padding:1.8rem 1.8rem .4rem;
  grid-template-columns:repeat(auto-fit, minmax(15rem, 1fr));
}
/* MEDIDO em 12/08 na HOME, que é a primeira tela que qualquer visitante vê: os 17
   cartões estavam com `box-shadow: none` em repouso — mais planos que os painéis das
   páginas internas, que já usam `--shadow-card` + `--panel-inset`. Todo o volume deles
   (giro, camadas em translateZ, luz seguindo o ponteiro) mora sob `html.tem-3d`, que só
   entra com mouse fino E sem `prefers-reduced-motion`. Ou seja: no celular e para quem
   pediu menos movimento, a vitrine do site era um retângulo vazio.
   As mesmas variáveis do resto do site entram aqui como base ESTÁTICA — profundidade
   para todo mundo; o giro continua exclusivo do tem-3d, logo abaixo. */
.home-card{
  display:flex; flex-direction:column; gap:.35rem; padding:1.1rem 1.2rem;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg);
  text-decoration:none; position:relative; overflow:hidden;
  transition:border-color .18s ease, transform .18s ease, box-shadow .18s ease;
  box-shadow:var(--shadow-card), var(--panel-inset);
}
/* Filete que acende no hover — em vez de mover o cartão inteiro pra longe. */
.home-card::after{
  content:""; position:absolute; left:0; right:0; top:0; height:2px;
  background:var(--icon-tint, var(--accent)); opacity:0; transition:opacity .18s ease;
}
/* O `--panel-inset` continua no hover: sem ele o cartão PERDIA o fio de luz da aresta de
   cima justo quando sobe — subir e achatar ao mesmo tempo é a leitura errada. */
.home-card:hover{ border-color:color-mix(in oklch, var(--icon-tint, var(--accent)) 55%, var(--border)); transform:translateY(-2px); box-shadow:var(--shadow-soft), var(--panel-inset); }
.home-card:hover::after{ opacity:.9; }

/* ── volume nos cartões ──
   Só entra quando o JS confirma que há mouse fino e que a pessoa não pediu menos
   movimento (html.tem-3d). Sem a classe, tudo abaixo é ignorado e o cartão continua
   exatamente como era — a versão plana não depende de nada disto.

   A perspectiva mora no CONTAINER, não no cartão: assim todos os cartões da grade
   compartilham o mesmo ponto de fuga e a fileira parece um plano só, em vez de oito
   caixas cada uma com a sua própria câmera. */
/* O ponto de fuga acompanha o ponteiro (shared.js escreve --fuga-x/--fuga-y). Os valores
   padrão são os antigos, fixos: se o JS não rodar, a cena volta a ser vista do centro,
   exatamente como antes. `transition` para a câmera não saltar entre um cartão e outro
   quando o ponteiro cruza o vão da grade. */
.tem-3d .home-cards{
  /* ── a perspectiva acompanha a LARGURA DA TELA, e não um valor fixo ──
     MEDIDO em 30/08, com os números que o próprio navegador do operador reportou (tela
     2560x1080, Chrome 152, GPU ligada): o ícone do primeiro cartão saía 25,6 px para fora
     do cartão. A conta bate exata: a camada voa `translateZ(26px)` e a perspectiva a
     projeta para longe do ponto de fuga na proporção `distância × z / (P − z)`. Com o
     ponto de fuga a 970 px do ícone e P=1000, isso dá 25,9 px — contra um padding de
     19,2 px. O ícone não tem como ficar dentro.
     Em 4 colunas o efeito é lateral: os cartões da COLUNA 1 ficam ~1226 px do ponto de
     fuga e deslocam ~33 px para a esquerda, enquanto os do meio quase não se movem. Era
     isso que aparecia na tela: só a primeira coluna com o ícone montado na borda.
     `perspective` fixo não tem como funcionar aqui, porque a distância cresce junto com o
     monitor e o padding não. Em `vw` os dois lados da fração crescem juntos e o
     deslocamento fica constante em ~10 px, em qualquer tela — folgado dentro dos 19,2 px.
     O relevo fica mais suave, e isso é ganho: o comentário do shared.js já registrava que
     com paralaxe forte "os cartões da ponta parecem tombar".
     NÃO reproduzia no meu navegador: sem GPU, o Chrome não aplica essa projeção nem ao
     desenho nem ao getBoundingClientRect. Os valores vieram da máquina do operador. */
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .home-card{
  /* ── por que o overflow volta a `visible` aqui ──
     `overflow` diferente de `visible` é "grouping property": força `transform-style: flat`
     e vence o `preserve-3d` da linha de baixo. MEDIDO em 13/08: as três camadas deste
     cartão — ícone, título e descrição — mediam **0,00%** de escala com `translateZ`; com
     `overflow:visible`, o ícone vai a 2,67%. As camadas estavam escritas desde que o
     cartão ganhou 3D e NUNCA existiram na tela. Somado ao giro que também não acontecia
     (corrigido no ciclo 50), o cartão-assinatura do sistema estava inteiramente inerte:
     só a luz e a sombra, que vêm de `box-shadow`, funcionavam.

     O `overflow:hidden` da regra base FICA — ele tem uma função real, que é fazer o filete
     do `::after` seguir a curva do cartão. Quem paga essa conta aqui é o próprio filete,
     na regra logo abaixo. */
  overflow:visible;
  transform-style:preserve-3d;
  transition:border-color .18s ease, box-shadow .18s ease, transform .25s cubic-bezier(.22,.61,.36,1);
}
/* `will-change` só ENQUANTO O PONTEIRO ESTÁ NA GRADE, e não o tempo todo.
   Ele existe para promover o cartão a camada própria antes do primeiro giro, senão o
   movimento engasga enquanto a textura é criada — isso continua valendo. O que muda é o
   escopo: aplicado na regra do cartão, ele mantinha os DEZESSETE cartões da home
   promovidos a camadas de composição permanentes, mesmo com o ponteiro do outro lado da
   tela ou o site parado numa aba de fundo.
   Isso é o que a própria spec desaconselha, e o comentário do body::after (lá em cima,
   perto da linha 239) já registra a regra: "will-change não fica em elemento que não vai
   animar". Camada promovida é também onde aparecem artefatos de pintura que dependem de
   GPU e driver — o tipo de defeito que aparece na máquina de uma pessoa e não na de
   outra.
   `:hover` na GRADE dispara antes do `:hover` do cartão (o ponteiro cruza o vão primeiro),
   então a promoção continua acontecendo antes do giro começar. Fora da grade, zero
   camadas extras. */
.tem-3d .home-cards:hover .home-card{ will-change:transform; }
.tem-3d .home-card:hover{
  /* As variáveis vêm do ponteiro (shared.js) e caem para 0 quando ele sai, então esta
     mesma regra serve para inclinar e para voltar ao plano. O translateY continua: é a
     elevação que diz "isto é clicável", o giro só acrescenta o relevo.

     O `!important` foi acrescentado em 13/08 porque este giro NUNCA ACONTECEU: o
     `.home-card` está na lista da animação `surgir`, que também anima `transform`, e
     animação vence regra normal na cascata. O efeito-assinatura do sistema 3D esteve
     morto desde que foi escrito, e ninguém viu porque a luz e a elevação (que vêm de
     `box-shadow`, não de `transform`) continuavam funcionando — o cartão reagia ao
     ponteiro, só não girava.

     Achado pelo guardião, não a olho: ao generalizar a verificação para cruzar "quem roda
     a surgir" com "quem gira no hover", o `.home-card` apareceu sozinho na lista. */
}
.tem-giro .home-card:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) !important; }
/* Conteúdo à frente do fundo do cartão. É o que separa relevo de imagem torta: sem a
   profundidade entre as camadas, girar um retângulo plano só entorta o retângulo. */
/* O filete do topo perdeu o clip do cartão junto com o `overflow`, e sem ele iria até as
   bordas retas — aparecendo por cima da área onde o cartão já curvou. Recuá-lo pelo raio
   o mantém dentro da faixa reta. MEDIDO: a curva de 8 px já comia até 8,0 px de cada
   ponta do filete no topo dele (e 2,7 px na base), então o recuo aqui devolve quase o
   mesmo desenho — a diferença fica abaixo de 6 px numa peça de 2 px de altura. */
.tem-3d .home-card::after{ left:var(--r-lg); right:var(--r-lg); }
.tem-3d .home-card-icon{ transform:translateZ(26px); }
.tem-3d .home-card-title{ transform:translateZ(16px); }
.tem-3d .home-card-desc{ transform:translateZ(8px); }
/* Brilho que segue o ponteiro, como luz batendo na face inclinada. Fica atrás do texto
   (z-index/ordem) e não intercepta clique. */
/* Mesma correção de luminância do painel (ciclo anterior), mas aqui a cor NÃO pode virar
   `--luz-cor`: cada cartão tem a sua (`--icon-tint`), e essa identidade é o ponto. Então
   o clareamento entra por fora — a tinta do cartão é misturada com branco na proporção
   `--luz-branco`, que é 85% no claro e 0% no escuro.
   MEDIDO antes: no claro o desvio era **-10,4 pontos** (pior que os -4,7 dos painéis,
   porque o alfa aqui é 22%): os 17 cartões da home ESCURECIAM ao receber o ponteiro. */
.tem-3d .home-card::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  transition:opacity .2s ease;
  background:radial-gradient(circle at var(--luz-x, 50%) var(--luz-y, 50%),
    color-mix(in oklch,
      color-mix(in oklch, oklch(100% 0 0) var(--luz-branco, 0%), var(--icon-tint, var(--accent)))
      var(--luz-alfa-card, 22%), transparent),
    transparent 60%);
}
.tem-3d .home-card:hover::before{ opacity:1; }

/* Cinto de segurança: se o navegador respeitar a preferência mas o JS não tiver rodado
   (falha de script, cache velho), o CSS ainda desliga o efeito por conta própria. */
@media (prefers-reduced-motion: reduce){
  /* `!important` aqui é consequência direta do `!important` no giro: uma regra normal não
     desliga uma que tem `!important`, nem estando dentro de uma media query. Sem isto,
     acrescentar a marca lá em cima teria entregado o giro justamente a quem pediu para
     não vê-lo. */
  /* Só o giro sai; as camadas em translateZ ficam (ver o bloco do .summary, linha ~476). */
  .tem-3d .home-card, .tem-3d .home-card:hover{ transform:none !important; transition:none; }
}
.home-card-title{ font-family:var(--font-display); color:var(--text); font-size:1.02rem; font-weight:600; letter-spacing:.01em; }
.home-card-desc{ color:var(--muted); font-size:.83rem; line-height:1.5; }
/* Os emoji do HTML são dessaturados e recolocados numa pastilha tingida pelo
   --icon-tint de cada cartão: continuam legíveis como ícone, mas param de
   brigar com a paleta fria. */
.home-card-icon{
  display:inline-flex; align-items:center; justify-content:center;
  width:2.3rem; height:2.3rem; border-radius:var(--r-md); font-size:1.1rem; margin-bottom:.5rem;
  background:color-mix(in oklch, var(--icon-tint, var(--accent)) 14%, transparent);
  border:1px solid color-mix(in oklch, var(--icon-tint, var(--accent)) 32%, transparent);
  filter:saturate(.25) contrast(1.05);
}
.home-card:hover .home-card-icon{ filter:saturate(.75); }
@media (min-width: 64rem){
  .home-cards{ grid-template-columns:repeat(4, 1fr); }
  .home-card:first-child{
    grid-column:span 2; padding:1.5rem 1.6rem; justify-content:space-between;
    background:linear-gradient(150deg, var(--accent-soft), var(--surface) 70%);
    border-color:var(--border-strong);
  }
  .home-card:first-child .home-card-title{ font-size:1.45rem; }
  .home-card:first-child .home-card-icon{ width:2.8rem; height:2.8rem; font-size:1.4rem; }
}

.home-grid{
  display:grid; gap:1rem; padding:0 1.8rem 3rem;
  grid-template-columns:repeat(auto-fit, minmax(20rem, 1fr));
}
.home-news-section{ padding:0 1.8rem 1.2rem; }
/* 5 notícias empilhadas em largura total viraria uma parede de texto; auto-fit
   devolve pra 1 coluna no mobile. */
.home-news-section #home-news{
  display:grid; gap:.8rem;
  grid-template-columns:repeat(auto-fit, minmax(19rem, 1fr));
}
/* ── calendário de eventos ── */
.ev-titulo{ text-transform:capitalize; min-width:12ch; text-align:center; }
.ev-destaque{ border-left:3px solid var(--accent); text-align:left; }
.ev-destaque-nome{ font-size:1.05rem; line-height:1.25; }
.ev-destaque-desc{ margin:.4rem 0 0; }
.ev-item{ padding:.85rem 0; border-bottom:1px solid var(--border); }
.ev-item:last-child{ border-bottom:none; }
/* O que está valendo agora recebe o mesmo tratamento do resto do site para "ativo":
   filete de acento à esquerda, sem gritar. */
.ev-item-ativo{ border-left:3px solid var(--accent); padding-left:.8rem; background:var(--accent-soft); }
.ev-cab{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }
.ev-cor{ width:10px; height:10px; border-radius:50%; flex:0 0 auto; box-shadow:inset 0 0 0 1px oklch(0% 0 0 / .25); }
.ev-nome{ font-size:.98rem; }
.ev-selo{
  font-family:var(--font-mono); font-size:.6rem; text-transform:uppercase; letter-spacing:.08em;
  padding:.1rem .5rem; border-radius:var(--r-pill); background:var(--surface-2, var(--surface));
  color:var(--muted); border:1px solid var(--border);
}
.ev-selo-ativo{ background:var(--accent-soft); color:var(--accent); border-color:transparent; font-weight:700; }
/* Distingue os homônimos (três "Castle" no mesmo mês, um por tipo de PVP). Tom neutro:
   é qualificador do nome, não estado do evento — quem manda no destaque é o selo. */
.ev-qualificador{
  font-family:var(--font-mono); font-size:.62rem; font-weight:600;
  padding:.05rem .4rem; border-radius:var(--r-sm);
  background:var(--surface); border:1px solid var(--border); color:var(--muted);
  white-space:nowrap;
}
.ev-quando{ margin-top:.15rem; }
.ev-desc{ margin:.35rem 0 0; font-size:.86rem; line-height:1.55; }

/* ── impulsionados do dia: faixa de UMA linha no topo ──
   Mora dentro do hero. Não é painel: sem moldura pesada, sem título próprio, altura de
   uma linha de texto. No topo o espaço vertical é o mais caro da página, e isto aqui é
   uma informação de duas palavras que precisa estar à vista. */
/* A faixa dos impulsionados fica DENTRO do hero, logo abaixo do título que já se levanta
   do papel — e era a única superfície da região ainda sem volume. Relevo leve, porque
   ela é uma tira de 41 px e não deve competir com os cartões abaixo. */
.boosted-faixa{
  display:flex; align-items:center; flex-wrap:wrap; gap:.45rem .9rem;
  margin-top:.9rem; padding:.4rem .7rem;
  border:1px solid var(--border); border-left:2px solid var(--accent);
  border-radius:var(--r-sm); background:var(--surface);
  font-size:.8rem; line-height:1.3;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .boosted-faixa{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .38);
}
.boosted-tag{
  font-family:var(--font-mono); font-size:.6rem; text-transform:uppercase; letter-spacing:.09em;
  color:var(--muted); font-weight:700;
}
.boosted-item{ display:inline-flex; align-items:center; gap:.4rem; min-width:0; }
/* 26px: a altura de uma linha de texto. A figura acompanha o texto em vez de mandar na
   altura da faixa. `pixelated` porque o sprite é arte de pixel — suavizar vira borrão.

   MEDIDO em 13/08: a faixa tinha 2 camadas de sombra e os sprites dentro dela, `filter:
   none` — relevo no contêiner e nada dentro, a mesma incoerência que a página de status
   tinha. E é justo aqui que dói: o boss e a criatura do dia são o que o site de
   referência põe no alto da página dele, com figura de cada um.

   O `drop-shadow` (e não `box-shadow`) porque a sombra tem que seguir o CONTORNO do
   sprite, não a caixa dele — um monstro recortado com sombra retangular fica colado num
   cartão invisível. Mesmos valores do `.charm-icon`, que resolveu este caso primeiro. */
.boosted-fig{
  width:26px; height:26px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto;
  filter:drop-shadow(0 1px 1px oklch(0% 0 0 / .38));
}
html[data-theme="dark"] .boosted-fig{ filter:drop-shadow(0 1px 1.5px oklch(0% 0 0 / .6)); }
.boosted-rotulo{
  font-family:var(--font-mono); font-size:.6rem; text-transform:uppercase; letter-spacing:.08em;
  color:var(--accent); font-weight:700;
}
.boosted-nome{ font-weight:600; overflow-wrap:anywhere; }
/* No celular a etiqueta "IMPULSIONADOS DE HOJE" consumia uma linha inteira só para
   rotular — medido: 74 px em duas linhas contra 41 px no desktop. Os rótulos "Boss" e
   "Criatura" já dizem o que cada coisa é, então a etiqueta sai e a faixa volta a caber
   em uma linha. */
@media (max-width:560px){
  .boosted-tag{ display:none; }
  .boosted-faixa{ gap:.35rem .7rem; font-size:.75rem; }
  .boosted-fig{ width:22px; height:22px; }
}

.news-item{ padding:.7rem 0; border-bottom:1px solid var(--border); }
.news-item:last-child{ border-bottom:none; }
/* MEDIDO em 12/08 na HOME: estes 5 cartões (379×184) estavam com `box-shadow: none` ao
   lado dos 17 `.home-card`, que ganharam 3 camadas no ciclo 23. Mesma página, mesma
   função de cartão, um com volume e o outro colado no papel.
   Usa as variáveis do sistema em vez de valores próprios: é o mesmo objeto. */
.home-news-section .news-item{
  padding:1rem 1.1rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); border-top:2px solid var(--accent);
  box-shadow:var(--shadow-card), var(--panel-inset);
}
.news-meta{ display:flex; align-items:center; gap:.5rem; margin-bottom:.3rem; }
/* Pastilha de categoria: mesma anatomia (e agora os mesmos valores) das outras pastilhas
   do site. Era a única do sistema ainda chapada. */
.news-cat{
  font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.08em;
  padding:.1rem .5rem; border:none; background:var(--accent-soft); color:var(--accent);
  border-radius:var(--r-pill); font-weight:700;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .news-cat{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 2px oklch(0% 0 0 / .35);
}
.news-text{ margin:0; font-size:.86rem; line-height:1.55; }

/* Aviso de cobertura parcial: visível, mas não alarmante — é estado normal
   enquanto a coleta percorre o diretório. */
.coverage-note{ margin:0; padding:0 1.8rem 1rem; font-size:.8rem; color:var(--muted); max-width:80ch; }
.coverage-note.partial{ color:var(--warn); }

/* ═══ perfil de guilda ═══ */
.guild-head{ padding:1.6rem 1.8rem .2rem; }
.guild-title{ font-family:var(--font-display); color:var(--text); margin:0 0 .3rem; font-size:1.8rem; font-weight:700; letter-spacing:-.01em; }
/* pre-wrap preserva as quebras que os jogadores escrevem, sem interpretar HTML
   de terceiro. */
.guild-desc{ white-space:pre-wrap; max-width:60ch; color:var(--muted); font-size:.86rem; margin:.6rem 0 0; }
.online-pill{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 35%, transparent); }

/* ═══ perfil de mundo ═══ */
.world-switch{ display:inline-flex; align-items:center; gap:.5rem; margin-top:.7rem; }
.world-switch select{ font-size:.85rem; }
.world-hero{ padding-bottom:1rem; }
.w-killer{ color:var(--offline); font-weight:600; margin-left:.5rem; }
/* Texto e valor dividem a primeira linha; a barra de força relativa fica num
   traço fino embaixo — assim um valor de 12 dígitos não empurra nada. */
.w-row{ padding:.4rem 0; border-bottom:1px solid color-mix(in oklch, var(--border) 40%, transparent); }
.w-row:last-child{ border-bottom:none; }
.w-row-top{ display:flex; align-items:baseline; justify-content:space-between; gap:.6rem; }
.w-row-label{ min-width:0; font-size:.82rem; }
.w-row-label .muted{ display:block; font-size:.7rem; }
.w-row-value{ flex:0 0 auto; font-weight:600; font-size:.8rem; white-space:nowrap; }
/* Relevo na regra agrupada das barras (a que começa em `.ha-barra`), inclusive a
   calibragem própria dos 3 px de altura. */
.w-bar{ display:block; height:3px; margin-top:.35rem; border-radius:var(--r-pill); background:color-mix(in oklch, var(--border) 45%, transparent); }
.w-bar-fill{ display:block; height:100%; border-radius:var(--r-pill); background:var(--bar-grad); }
/* Herda a cor do contexto: o sublinhado pontilhado é que sinaliza o clique, sem
   transformar a coluna inteira num campo de links azuis. */
a.world-link{
  color:inherit; text-decoration:underline; text-decoration-style:dotted;
  text-underline-offset:2px; text-decoration-color:color-mix(in oklch, currentColor 45%, transparent);
}
a.world-link:hover, a.world-link:focus-visible{ color:var(--accent); text-decoration-style:solid; }

.guild-tag{
  display:inline-block; margin-left:.4rem; font-family:var(--font-mono); font-size:.64rem; padding:.08rem .45rem;
  border-radius:var(--r-pill); background:var(--accent-soft); color:var(--accent);
  vertical-align:middle; white-space:nowrap;
}
a.guild-tag{ text-decoration:none; }
a.guild-tag:hover, a.guild-tag:focus-visible{ background:var(--accent); color:var(--on-accent); }
/* Guilda rastreada continua distinguível agora que todas aparecem no ranking. */
/* O contorno de "rastreada" SOMA com o relevo em vez de substituí-lo: esta regra tem
   especificidade maior que a das pastilhas, então sem repetir as duas camadas aqui a
   guilda rastreada seria a única etiqueta plana da lista. */
a.guild-tag-tracked{
  font-weight:700;
  box-shadow:
    inset 0 0 0 1px var(--accent),
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] a.guild-tag-tracked{
  box-shadow:
    inset 0 0 0 1px var(--accent),
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 2px oklch(0% 0 0 / .35);
}

.check-inline{
  display:inline-flex; align-items:center; gap:.4rem; font-size:.82rem;
  color:var(--muted); white-space:nowrap; cursor:pointer;
}
.check-inline input{ cursor:pointer; }

.voc-pill{
  display:inline-block; font-family:var(--font-mono); font-size:.66rem; font-weight:500; padding:.12rem .5rem;
  border-radius:var(--r-pill); white-space:nowrap;
  background:color-mix(in oklch, var(--voc-color, var(--muted)) 14%, transparent);
  color:var(--voc-color, var(--muted));
  border:1px solid color-mix(in oklch, var(--voc-color, var(--muted)) 38%, transparent);
}
.voc-pill.voc-knight{ --voc-color:var(--voc-knight); }
.voc-pill.voc-paladin{ --voc-color:var(--voc-paladin); }
.voc-pill.voc-druid{ --voc-color:var(--voc-druid); }
.voc-pill.voc-sorcerer{ --voc-color:var(--voc-sorcerer); }
.voc-pill.voc-monk{ --voc-color:var(--voc-monk); }
.voc-pill.voc-none{ --voc-color:var(--voc-none); }
.exp-value{ color:var(--accent-strong); font-weight:600; }
.delta-pos{ color:var(--online); font-weight:600; }
.delta-neg{ color:var(--offline); font-weight:600; }
.muted{ color:var(--muted); }
.empty{ text-align:center; color:var(--muted); padding:2rem 0; }
#clear-filters{
  border:1px solid var(--border); background:var(--surface-2);
  color:var(--accent-strong); border-radius:var(--r-md); padding:.35rem .8rem; font-size:.76rem; cursor:pointer;
  margin-left:.5rem; transition:border-color .15s ease;
}
#clear-filters:hover{ border-color:var(--accent); }

@keyframes skeleton-pulse{ 0%,100%{ opacity:.45; } 50%{ opacity:.9; } }
.skeleton-line{
  height:.7rem; border-radius:var(--r-sm); background:var(--surface-3);
  animation:skeleton-pulse 1.4s ease-in-out infinite; margin:.35rem 0;
}
.skeleton-line.short{ width:45%; height:.6rem; }
.card.skeleton .skeleton-line:last-child{ width:70%; height:1.3rem; }
.compare-card.skeleton .skeleton-line{ width:100%; }
.loading-row td{ text-align:center; color:var(--muted); padding:1.5rem 0; }
@media (prefers-reduced-motion: reduce){ .skeleton-line{ animation:none; } }

/* ═══ login: botão + modal ═══ */
.auth-button{
  border:1px solid var(--border); background:var(--surface-2);
  border-radius:var(--r-md); padding:0 .8rem; height:2.15rem; font-size:.8rem; font-weight:600; color:var(--text);
  cursor:pointer; transition:border-color .15s ease, box-shadow .15s ease; white-space:nowrap;
  font-family:var(--font-body);
}
/* O hover deste botão está junto com o do .theme-toggle, lá no bloco da barra superior:
   os dois são a mesma tecla e vinham repetindo o halo em dois lugares do arquivo. */
.auth-button:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

.login-required{ color:var(--muted); font-size:.82rem; margin:.5rem 0 0; }
.link-button{ border:none; background:none; padding:0; color:var(--accent-strong); font-weight:600; cursor:pointer; text-decoration:underline; font-size:inherit; font-family:inherit; }
.link-button:hover{ color:var(--accent); }
.link-button:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

.login-overlay{
  position:fixed; inset:0; background:oklch(10% .02 258 / .62); backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px);
  display:flex; align-items:center; justify-content:center; z-index:50; padding:1rem;
}
.login-overlay[hidden]{ display:none; }
/* O modal é a peça que PAIRA MAIS ALTO do site (dentro do overlay z50), e era a última
   ainda com a sombra de uma camada só. `--shadow-flutuante` já traz o bisel, então o
   `--panel-inset` sai daqui — sem isso, a mesma aresta seria desenhada duas vezes. */
.login-modal{
  background:var(--card-gradient);
  border:1px solid var(--border-strong); border-radius:var(--r-lg); padding:1.5rem 1.6rem;
  width:min(340px, 100%); box-shadow:var(--shadow-flutuante);
}
.login-modal h2{ font-size:1.05rem; margin:0 0 1rem; text-transform:uppercase; letter-spacing:.1em; }
.login-modal label{ display:block; font-family:var(--font-mono); font-size:.66rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin:.8rem 0 .3rem; }
.login-modal input[type="text"], .login-modal input[type="password"]{
  width:100%; background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r-md);
  padding:.5rem .7rem; color:var(--text); font-size:.85rem; box-sizing:border-box;
}
.login-remember{ display:flex !important; align-items:center; gap:.4rem; font-size:.8rem !important; margin-top:.8rem !important; color:var(--text) !important; text-transform:none !important; letter-spacing:0 !important; font-family:var(--font-body) !important; }
.login-remember input{ width:auto; }
.login-error{ color:var(--offline); font-size:.82rem; margin:.7rem 0 0; }
.login-actions{ display:flex; justify-content:flex-end; gap:.5rem; margin-top:1.2rem; }
.login-actions button{ border-radius:var(--r-md); padding:.5rem 1.1rem; font-size:.78rem; font-weight:700; cursor:pointer; }
#login-cancel{ border:1px solid var(--border); background:var(--surface-2); color:var(--text); }
#login-cancel:hover{ border-color:var(--border-strong); }
.login-modal *:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* Lista de invariantes quebradas (Status). Só aparece quando há problema. */
.integridade-lista{ margin-top:.7rem; display:flex; flex-direction:column; gap:.7rem; }
.integridade-item{
  display:flex; flex-direction:column; gap:.15rem;
  padding:.5rem .6rem; border-left:2px solid var(--offline);
  background:var(--surface-2); border-radius:0 var(--r-md) var(--r-md) 0;
}
.integridade-item b{ color:var(--offline); font-size:.85rem; }
.integridade-exemplos{ font-size:.7rem; color:var(--muted); word-break:break-word; }

/* ═══ favoritos ("Meu Vigia") + hovercard ═══ */
.fav-star{
  background:none; border:none; cursor:pointer; line-height:1;
  font-size:1.1rem; color:var(--muted); padding:.1rem .25rem;
  border-radius:var(--r-sm); transition:color .15s, transform .1s;
}
.fav-star:hover{ color:var(--accent); transform:scale(1.15); }
.fav-star.is-fav{ color:var(--accent); }
.fav-star:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.fav-star-profile{ font-size:1.45rem; vertical-align:middle; margin-left:.4rem; }
.home-favorites-section{ margin:1rem 0; padding:0 1.8rem; }
#home-favorites{ display:grid; gap:.6rem; grid-template-columns:repeat(auto-fill, minmax(17rem, 1fr)); }
.fav-row{
  display:flex; align-items:center; gap:.5rem; flex-wrap:wrap;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface-2); padding:.65rem .8rem;
}
.fav-name{ font-weight:600; }
.fav-meta{ flex:1 1 auto; }
.fav-status{ display:inline-flex; gap:.5rem; align-items:center; margin-left:auto; }
.fav-online{ color:var(--online); font-weight:600; font-size:.82rem; }
.fav-offline{ color:var(--muted); font-size:.82rem; }
.fav-star-inline{ font-size:.9rem; padding:0 .15rem; vertical-align:middle; }
/* MEDIDO em 13/08: este era o último alternador do site sem relevo — `box-shadow: none`
   nos DOIS estados, e ligado x desligado se distinguia só por cor. Um alternador que não
   afunda nem levanta é uma tecla pintada. Segue o mesmo desenho do `.baz-toggle`: relevo
   de tecla na base e, no estado ligado, um anel de acento que SOMA com esse relevo. */
.notif-toggle{
  background:var(--surface-2); border:1px solid var(--border); color:var(--text);
  border-radius:var(--r-md); padding:.28rem .6rem; font-size:.76rem; cursor:pointer;
  font-family:inherit;
  transition:border-color .15s, color .15s, box-shadow .15s ease, transform .1s ease;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .4),
    inset 0 -1px 0 oklch(0% 0 0 / .12),
    0 1px 2px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .notif-toggle{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    inset 0 -1px 0 oklch(0% 0 0 / .25),
    0 1px 2px oklch(0% 0 0 / .35);
}
.notif-toggle:hover{ border-color:var(--accent); }
/* O anel entra SOMANDO ao relevo, não no lugar dele — se substituísse, o estado ligado
   (justamente o que precisa se destacar) seria o único plano. É a mesma armadilha que o
   comentário do `.baz-toggle` registra. Aqui o anel é `--online` porque é a cor que este
   botão já usa para dizer "avisos ligados". */
.notif-toggle.is-on{
  border-color:var(--online); color:var(--online);
  box-shadow:
    inset 0 0 0 1px var(--online),
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 2px oklch(38% .06 250 / .2);
}
html[data-theme="dark"] .notif-toggle.is-on{
  box-shadow:
    inset 0 0 0 1px var(--online),
    inset 0 1px 0 oklch(100% 0 0 / .1),
    0 1px 2px oklch(0% 0 0 / .38);
}
/* Afunda ao ser apertado, como todas as outras teclas do site. */
.notif-toggle:active{
  transform:translateY(1px);
  box-shadow:inset 0 2px 3px oklch(0% 0 0 / .22);
}
@media (prefers-reduced-motion: reduce){
  .notif-toggle:active{ transform:none; }
}
.fav-filter-btn{
  position:fixed; right:1.1rem; bottom:1.1rem; z-index:50;
  display:inline-flex; align-items:center; gap:.35rem;
  background:var(--surface); color:var(--text);
  border:1px solid var(--border-strong); border-radius:var(--r-pill);
  padding:.55rem .95rem; font-family:inherit; font-size:.82rem; font-weight:600;
  /* Este é o único elemento do site que é TECLA e PAIRA ao mesmo tempo (fixo, z50): leva
     as duas coisas — relevo de tecla nas arestas e sombra de elevação embaixo. Com só a
     `--shadow-soft` de uma camada ele parecia um adesivo grudado na janela. */
  cursor:pointer;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    inset 0 -1px 0 oklch(0% 0 0 / .12),
    0 2px 4px oklch(38% .06 250 / .22),
    0 10px 22px -12px oklch(38% .06 250 / .45);
  transition:border-color .15s, color .15s, background .15s, box-shadow .15s;
}
html[data-theme="dark"] .fav-filter-btn{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    inset 0 -1px 0 oklch(0% 0 0 / .28),
    0 2px 5px oklch(0% 0 0 / .45),
    0 12px 26px -12px oklch(0% 0 0 / .55);
}
/* Apertar encosta a tecla na superfície: o relevo vira sulco e a elevação some. */
.fav-filter-btn:active{ box-shadow:inset 0 2px 4px oklch(0% 0 0 / .28); }
.fav-filter-btn:hover{ border-color:var(--accent); }
.fav-filter-btn.is-on{ border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }
.fav-filter-count{ color:var(--muted); font-weight:400; }
/* O display:inline-flex acima sobrepõe o [hidden] do navegador — sem esta regra
   o botão apareceria em páginas sem tabela de nomes. */
.fav-filter-btn[hidden]{ display:none; }

.char-hovercard{
  position:absolute; z-index:60; min-width:200px; max-width:280px;
  background:var(--surface); border:1px solid var(--border-strong);
  border-radius:var(--r-md); box-shadow:var(--shadow-flutuante);
  padding:.65rem .8rem; font-size:.84rem; color:var(--text);
}
.char-hovercard .hc-name{
  font-weight:700; font-size:.92rem; margin-bottom:.25rem;
  display:flex; gap:.4rem; align-items:baseline; flex-wrap:wrap;
}
.char-hovercard .hc-line{ color:var(--muted); margin:.1rem 0; }
.char-hovercard .hc-line strong{ color:var(--text); }
.char-hovercard .hc-online{ color:var(--online); font-size:.76rem; font-weight:600; }
.char-hovercard .hc-offline{ color:var(--muted); font-size:.76rem; }

/* ═══ pulso do servidor ═══ */
.pulse-main{ max-width:1100px; margin:0 auto; padding:0 1rem 2.5rem; }
.pulse-totais{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(190px, 1fr));
  gap:.8rem; margin:1rem 0 1.5rem;
}
.pulse-tile{
  background:var(--card-gradient); border:1px solid var(--border);
  border-radius:var(--r-lg); padding:1rem 1.15rem; box-shadow:var(--shadow-card);
  position:relative;
}
.pulse-tile::after{
  content:""; position:absolute; left:0; top:0; width:2px; height:100%;
  background:linear-gradient(180deg, var(--accent), transparent 70%);
  border-radius:var(--r-lg) 0 0 var(--r-lg);
}
.pulse-tile-value{ font-size:1.8rem; font-weight:700; color:var(--text); line-height:1.1; letter-spacing:-.02em; }
.pulse-tile-label{ color:var(--muted); font-size:.8rem; margin-top:.3rem; }

/* ── volume nos ladrilhos do pulso ──
   Mesma estrutura do cartão de resumo (número grande, rótulo embaixo) e mesmo tratamento:
   o número é o conteúdo, o rótulo é a legenda dele.

   A luz vai no `::before` porque o `::after` já é a barra de acento da aresta esquerda.
   E essa barra fica NO PLANO de propósito: é uma marca pintada na superfície do ladrilho,
   não uma peça pousada sobre ele — mesma leitura que manteve o brasão da carta de guilda
   sem elevação. De quebra ela mora na borda, que é onde o deslocamento lateral da
   perspectiva mais castiga. */
.tem-3d .pulse-totais{
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .pulse-tile{
  transform-style:preserve-3d;
  will-change:transform;
}
/* `!important` pelo mesmo motivo medido no cartão de resumo: o `.pulse-tile` está na
   lista da animação `surgir`, que também anima `transform`, e animação ocupa uma casa
   mais alta da cascata que regra normal. Sem isto o giro não acontece. */
.tem-3d .pulse-tile:hover{
  transition:transform .25s cubic-bezier(.22,.61,.36,1), box-shadow .25s ease, border-color .25s ease;
  border-color:color-mix(in oklch, var(--accent) 38%, var(--border));
  box-shadow:var(--shadow-soft), var(--panel-inset);
}
/* `!important` pelo mesmo motivo medido no cartão de resumo: o `.pulse-tile` está na
   lista da animação `surgir`, que também anima `transform`, e animação ocupa uma casa
   mais alta da cascata que regra normal. Sem isto o giro não acontece. */
.tem-giro .pulse-tile:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) !important; }
.tem-3d .pulse-tile-value{ transform:translateZ(20px); }
.tem-3d .pulse-tile-label{ transform:translateZ(10px); }
.tem-3d .pulse-tile::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .2s ease;
  background:radial-gradient(circle at var(--luz-x, 50%) var(--luz-y, 50%),
    color-mix(in oklch,
      color-mix(in oklch, oklch(100% 0 0) var(--luz-branco, 0%), var(--accent))
      var(--luz-alfa-card, 22%), transparent),
    transparent 60%);
}
.tem-3d .pulse-tile:hover::before{ opacity:1; }

@media (prefers-reduced-motion: reduce){
  /* Só o giro sai; as camadas em translateZ ficam (ver o bloco do .summary, linha ~476). */
  .tem-3d .pulse-tile, .tem-3d .pulse-tile:hover{ transform:none !important; transition:none; }
}
.pulse-nota{ margin:1.4rem 0 0; }

/* ═══ entrada suave + estabilidade de layout ═══ */
/* Não re-anima nos polls: estes são CONTAINERS estáticos (só o conteúdo interno
   é trocado a cada ciclo). */
@keyframes surgir{ from{ opacity:0; transform:translateY(6px); } to{ opacity:1; transform:none; } }
.panel, .home-card, .pulse-tile, .profile-cards .card{ animation:surgir .28s ease both; }
@media (prefers-reduced-motion: reduce){
  .panel, .home-card, .pulse-tile, .profile-cards .card{ animation:none; }
  .home-card{ transition:none; }
  .home-card:hover{ transform:none; }
}
#home-exp, #home-levels, #home-guilds, #home-favorites{ min-height:9rem; }

/* ═══ estados de rede ═══ */
.state-empty, .state-error{ text-align:center; color:var(--muted); padding:1.5rem 0; }
.state-error{ color:var(--offline); }
.retry-btn{
  display:inline-block; margin-left:.5rem; border:1px solid var(--border);
  background:var(--surface-2); color:var(--accent-strong); border-radius:var(--r-md); padding:.3rem .75rem;
  font-size:.76rem; font-weight:700; font-family:inherit; cursor:pointer;
  transition:border-color .15s ease;
}
.retry-btn:hover{ border-color:var(--accent); }
.retry-btn:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.retry-btn:disabled{ opacity:.6; cursor:progress; }
.offline-pill{
  position:fixed; left:50%; bottom:1rem; transform:translateX(-50%); z-index:80;
  background:var(--offline); color:oklch(99% 0 0); border-radius:var(--r-pill);
  padding:.5rem 1.1rem; font-size:.82rem; font-weight:600; max-width:calc(100vw - 2rem);
  box-shadow:var(--shadow-flutuante); text-align:center;
}
.offline-pill[hidden]{ display:none; }
@media (prefers-reduced-motion: no-preference){ .offline-pill{ animation:surgir .25s ease both; } }

.calc-msg{ color:var(--offline); font-size:.85rem; font-weight:600; margin:.7rem 0 0; }
.calc-msg[hidden]{ display:none; }
/* Quando a página CONSEGUIU preencher e só está contando o que fez, vermelho de
   erro seria mentira sobre o que aconteceu. */
.calc-msg.calc-msg-info{ color:var(--muted); font-weight:500; }
.calc-field{ position:relative; display:block; }

/* ═══ acessibilidade ═══ */
/* z-index 100: é a peça MAIS ALTA do site, acima até dos modais, e era a única sem
   sombra. Ela literalmente desce de fora da tela para cima de tudo quando quem navega
   por teclado dá o primeiro Tab — e é o primeiro contato dessa pessoa com a página.
   Sem elevação, chegava como um retângulo pintado sobre o cabeçalho. */
.skip-link{
  position:absolute; top:0; left:0; z-index:100; text-decoration:none;
  background:var(--accent); color:var(--on-accent);
  padding:.6rem 1rem; border-radius:0 0 var(--r-md) 0; font-weight:700; font-size:.9rem;
  transform:translateY(-130%); transition:transform .15s ease;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .28),
    0 2px 4px oklch(0% 0 0 / .28),
    0 12px 26px -12px oklch(0% 0 0 / .5);
}
.skip-link:focus{ transform:translateY(0); outline:2px solid var(--text); outline-offset:-2px; }
@media (prefers-reduced-motion: reduce){ .skip-link{ transition:none; } }
/* Fallback global: qualquer elemento interativo mostra o anel do tema ao navegar
   por teclado. Regras mais específicas continuam vencendo. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--accent); outline-offset:2px;
}
.skip-target:focus{ outline:none; }
/* Fica no fluxo (é lido) mas não ocupa espaço visível. clip-path em vez de
   display:none, que tiraria o texto da árvore de acessibilidade. */
.sr-only{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

@keyframes pulse-glow{
  0%{ box-shadow:0 0 0 0 var(--online-soft), 0 0 4px var(--online); }
  70%{ box-shadow:0 0 0 6px transparent, 0 0 10px var(--online); }
  100%{ box-shadow:0 0 0 0 transparent, 0 0 4px var(--online); }
}
.status-dot.online, .dot.ok{ animation:pulse-glow 2s infinite; }
@media (prefers-reduced-motion: reduce){ .status-dot.online, .dot.ok{ animation:none; } }

/* ═══ rodapé ═══ */
/* MEDIDO em 12/08: o rodapé (1265×392, em todas as páginas) é a maior superfície do site
   e estava com `box-shadow: none`. Aqui ele NÃO se eleva — ao contrário: é o plano de
   baixo, o fundo onde a página termina. A sombra interna no topo é a que o conteúdo
   projeta ao se apoiar nele, e isso diz "acabou" melhor que qualquer linha divisória. */
.site-footer{
  margin-top:2.5rem; padding:2.4rem 1.8rem 2.8rem;
  border-top:1px solid var(--border); background:var(--surface-2);
  position:relative;
  box-shadow:inset 0 6px 10px -8px oklch(0% 0 0 / .3);
}
html[data-theme="dark"] .site-footer{
  box-shadow:inset 0 8px 14px -10px oklch(0% 0 0 / .6);
}
.site-footer::before{
  content:""; position:absolute; left:0; right:0; top:-1px; height:1px;
  background:linear-gradient(90deg, transparent, var(--accent) 30%, var(--accent) 70%, transparent); opacity:.4;
}
.footer-cols{
  display:grid; gap:1.6rem; grid-template-columns:repeat(auto-fit, minmax(11rem, 1fr));
  max-width:70rem;
}
.footer-cols h3{
  font-family:var(--font-mono); font-size:.64rem; text-transform:uppercase; letter-spacing:.16em; color:var(--muted);
  margin:0 0 .7rem; font-weight:500;
}
.footer-cols a{ display:block; font-size:.84rem; padding:.22rem 0; color:var(--text); text-decoration:none; }
.footer-cols a:hover{ color:var(--accent); }
.footer-nota{ margin:1.8rem 0 0; font-size:.76rem; color:var(--muted); max-width:46rem; line-height:1.6; }

/* ═══ presença + conta ═══ */
/* Selos informativos — a última família da barra sem relevo, junto com `.live-badge`
   (home) e `.ev-selo` (eventos). São pastilhas com a mesma anatomia de `.pill`, então
   levam os MESMOS valores: peça pousada, não retângulo pintado. */
.presenca-badge, .live-badge, .ev-selo{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .presenca-badge,
html[data-theme="dark"] .live-badge,
html[data-theme="dark"] .ev-selo{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 2px oklch(0% 0 0 / .35);
}
.presenca-badge{
  display:inline-flex; align-items:center; white-space:nowrap;
  padding:.22rem .6rem; border-radius:var(--r-pill);
  background:var(--online-soft); color:var(--online);
  border:1px solid color-mix(in oklch, var(--online) 30%, transparent);
  font-family:var(--font-mono); font-size:.72rem; font-weight:500;
}
.presenca-ponto{
  width:.45rem; height:.45rem; border-radius:50%; flex:none; margin-right:.45rem;
  background:var(--online); animation:pulse-glow 2s infinite;
}
@media (prefers-reduced-motion: reduce){ .presenca-ponto{ animation:none; } }
/* Espaço antes do separador vem daqui, não do texto: cada filho é um item flex,
   e item flex descarta espaço em branco na própria borda. */
.presenca-contas{ margin-left:.35rem; }
@media (max-width: 720px){ .presenca-contas{ display:none; } }

.conta-widget{ display:inline-flex; align-items:center; gap:.5rem; }
.conta-perfil{
  display:inline-flex; align-items:center; gap:.45rem; text-decoration:none;
  padding:.25rem .6rem .25rem .25rem; border-radius:var(--r-pill);
  border:1px solid var(--border); background:var(--surface-2); color:var(--text);
}
.conta-perfil:hover{ border-color:var(--accent); color:var(--accent-strong); }
.conta-avatar{
  display:inline-flex; align-items:center; justify-content:center;
  width:1.6rem; height:1.6rem; border-radius:50%; flex:none;
  background:var(--accent); color:var(--on-accent);
  font-weight:700; font-size:.78rem; font-family:var(--font-display);
}
.conta-nome{ font-size:.84rem; font-weight:600; max-width:9rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.conta-sair{ font-size:.76rem; padding:.35rem .7rem; border-radius:var(--r-md); }
.conta-abas{ display:flex; gap:2px; margin-bottom:1rem; padding:3px; background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r-md); }
.conta-aba{
  flex:1 1 auto; padding:.45rem .5rem; font-size:.76rem; border-radius:var(--r-sm);
  border:1px solid transparent; background:none; color:var(--muted); cursor:pointer; font-family:inherit;
}
.conta-aba:hover{ color:var(--text); background:var(--surface-3); }
.conta-aba.ativa{ background:var(--accent); border-color:transparent; color:var(--on-accent); font-weight:700; }
#conta-form{ display:flex; flex-direction:column; gap:.35rem; }
#conta-form label{ font-family:var(--font-mono); font-size:.66rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin-top:.6rem; }
.conta-dica{ font-size:.78rem; color:var(--muted); line-height:1.5; margin:.7rem 0 0; }
.conta-acoes{ display:flex; justify-content:flex-end; gap:.6rem; margin-top:1.1rem; }
.conta-msg{ margin:.8rem 0 0; font-size:.82rem; font-weight:600; color:var(--offline); }
.conta-msg[hidden]{ display:none; }
.conta-msg.conta-msg-ok{ color:var(--online); }
/* O código de recuperação é para ser LIDO e copiado à mão. Perder este código
   significa perder a conta. */
.conta-codigo{
  margin:1rem 0; padding:.9rem; border-radius:var(--r-md); text-align:center;
  font-size:1.1rem; letter-spacing:.1em; font-weight:700;
  background:var(--surface-3); border:1px dashed var(--accent); color:var(--accent-strong);
  user-select:all; word-break:break-all;
}
.conta-confirma{ margin-top:.8rem; font-size:.8rem; }

/* Página privada vista por quem não entrou. */
.acesso-restrito{ max-width:34rem; margin:4rem auto; padding:0 1.8rem; text-align:center; }
/* h2 desde que o título da página virou o h1 do .hero — o tamanho continua o mesmo,
   porque o peso visual aqui não depende do nível semântico. */
.acesso-restrito h2{ font-size:1.7rem; margin-bottom:.6rem; }
.acesso-acoes{ display:flex; gap:.7rem; justify-content:center; margin-top:1.4rem; }

/* ═══ PISO DE ESTILO DOS CONTROLES ═════════════════════════════════════════
   Todo select/input/textarea nasce com a identidade do site. Existe porque o
   CSS foi escrito ao redor dos ids do dashboard (#search, #export-csv) e as
   páginas de escopo servidor-inteiro criaram ids com prefixo (#rk-search,
   #ed-world, #gd-sort…): a barra de filtros ficava com caixas BRANCAS nativas
   do Windows dentro do tema escuro.

   ESPECIFICIDADE É A PARTE IMPORTANTE: seletor de elemento puro (0,0,1) e
   :where(), que não soma nada. Assim qualquer regra por classe/id continua
   vencendo, mesmo com este bloco no fim do arquivo. NÃO trocar por
   input[type="text"]: empata em (0,1,1) com as regras por classe e passa a
   ganhar por ordem, sobrescrevendo estilos que já estavam certos. */
select,
textarea,
input:where(:not([type="checkbox"], [type="radio"], [type="hidden"], [type="range"], [type="color"])){
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r-md);
  padding:.45rem .7rem; color:var(--text); font-size:.82rem; font-family:var(--font-body);
  transition:border-color .15s ease, box-shadow .15s ease;
  /* Campo de entrada é o oposto físico do botão: o botão é uma tecla em RELEVO, para ser
     apertada; o campo é uma cavidade, para receber alguma coisa. Os botões ganharam
     relevo num ciclo anterior e os campos ficaram chapados, então metade da metáfora
     estava no ar. MEDIDO em 12/08 só na página de Charms: 46 campos, TODOS com
     `box-shadow: none`.
     Sombra na aresta de cima, luz na de baixo — o inverso do relevo, que é o que faz
     um buraco parecer buraco. */
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .12),
    inset 0 -1px 0 oklch(100% 0 0 / .4);
}
/* `:where()` no prefixo do tema para a especificidade continuar (0,0,1), igual à regra
   acima. Sem ele, `html[data-theme="dark"] select` saltaria para (0,1,2) e passaria a
   atropelar regras por classe que já estavam certas — o motivo do comentário lá em cima. */
:where(html[data-theme="dark"]) select,
:where(html[data-theme="dark"]) textarea,
:where(html[data-theme="dark"]) input:where(:not([type="checkbox"], [type="radio"], [type="hidden"], [type="range"], [type="color"])){
  box-shadow:
    inset 0 1px 3px oklch(0% 0 0 / .32),
    inset 0 -1px 0 oklch(100% 0 0 / .05);
}
select:hover, textarea:hover,
input:where(:not([type="checkbox"], [type="radio"], [type="hidden"])):hover{ border-color:var(--border-strong); }
/* ESTA é a única regra de foco de campo do site. MEDIDO em 12/08 comparando declaração
   por declaração no CSSOM: havia mais 18 regras (`.calc-form input:focus`,
   `.charm-ordem select:focus`, `.bt-busca input:focus`…) **byte a byte idênticas** a esta
   — mesmo outline, mesma border-color, mesmo anel. Redundância pura, mas com
   especificidade de classe, então venciam e impediam qualquer evolução daqui.
   Removidas as 18, o campo pode continuar ESCAVADO enquanto está aceso: sem o inset, ele
   subia ao receber o foco e afundava ao perdê-lo — um movimento que ninguém pediu.
   O fio de luz de baixo sai de cena no foco: competiria com o anel de acento. */
select:focus, textarea:focus,
input:where(:not([type="checkbox"], [type="radio"], [type="hidden"])):focus{
  outline:none; border-color:var(--accent);
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .12),
    0 0 0 3px var(--accent-soft);
}
:where(html[data-theme="dark"]) select:focus,
:where(html[data-theme="dark"]) textarea:focus,
:where(html[data-theme="dark"]) input:where(:not([type="checkbox"], [type="radio"], [type="hidden"])):focus{
  box-shadow:
    inset 0 1px 3px oklch(0% 0 0 / .32),
    0 0 0 3px var(--accent-soft);
}
::placeholder{ color:var(--muted); opacity:.75; }
/* A largura padrão do <input> é intrínseca (~20 caracteres), e nas barras de
   filtro isso cortava o próprio placeholder. min() evita estouro no celular. */
input:where([type="search"], [type="url"]){ min-width:min(14rem, 100%); }
/* Seta própria: appearance:none apaga a nativa, então ela é redesenhada aqui
   (SVG embutido, sem requisição). currentColor não funciona em data URI — daí o
   stroke fixo num cinza-azulado que passa nos dois temas. */
select{
  appearance:none; padding-right:1.9rem;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237c8794' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right .65rem center; background-size:.6rem;
}
/* O menu aberto do <select> é desenhado pelo sistema, não pelo CSS da página:
   sem cor explícita nas <option> o Windows usa fundo branco e, no tema escuro,
   texto claro sobre branco fica ilegível. */
select option{ background:var(--surface); color:var(--text); }
input:where([type="checkbox"], [type="radio"]){ accent-color:var(--accent); }

/* Paginação (diretório de guildas). */
.pagination{ display:flex; gap:.4rem; align-items:center; justify-content:center; flex-wrap:wrap; margin:1.2rem 0; }
/* MEDIDO em 13/08: estes botões tinham cor do tema mas `box-shadow: none` e nenhum
   `:active` — os únicos do site que não pousavam nem afundavam. Todos os outros já eram
   peças físicas: bisel em cima, contato embaixo, e o clique empurra para dentro. */
.pagination button{
  border:1px solid var(--border); background:var(--surface-2); color:var(--muted);
  border-radius:var(--r-md); padding:.35rem .7rem; font-size:.78rem; cursor:pointer;
  font-family:var(--font-mono);
  transition:border-color .15s ease, color .15s ease, box-shadow .15s ease, transform .1s ease;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .16);
}
html[data-theme="dark"] .pagination button{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .34);
}
.pagination button:hover:not(:disabled){ border-color:var(--accent); color:var(--accent-strong); }
/* Afundar ao clicar: a sombra projetada dá lugar a uma interna, e a peça desce 1 px. É o
   mesmo gesto que `#export-csv` e `.btn-ghost` já faziam. */
.pagination button:active:not(:disabled){
  transform:translateY(1px);
  box-shadow:inset 0 2px 3px oklch(0% 0 0 / .22);
}
/* O deslocamento é movimento; a troca de sombra não. Quem pediu menos movimento continua
   vendo a peça afundar pela luz, só não a vê descer. */
@media (prefers-reduced-motion: reduce){
  .pagination button:active:not(:disabled){ transform:none; }
}
/* Desabilitado não pousa nem afunda: não há peça para apertar. */
.pagination button:disabled{ opacity:.45; cursor:default; box-shadow:none; }
.pagination button[aria-current="page"]{
  border-color:transparent; color:var(--on-accent); font-weight:700; background:var(--accent);
}
.pagination .pg-info{ color:var(--muted); font-size:.76rem; padding:0 .4rem; font-family:var(--font-mono); }

/* ══════════════════════════════════════════════════════════════════════════
   RADAR DE LEILÕES (bazaar)
   Cartões em vez de linhas: cada leilão carrega tempo restante, preço, perfil
   de skills e destaques — informação demais pra caber numa linha legível.
   ══════════════════════════════════════════════════════════════════════════ */
.baz-grade{
  display:grid; gap:.8rem; margin:1rem 0;
  /* Cartão de largura FIXA (340 px, a mesma do Exevo Pan), não elástica.
     Antes era minmax(300px, 1fr): em tela larga sobravam duas colunas de 660 px cada, e
     um cartão do dobro da largura espalha os mesmos dados por uma linha longa demais —
     o olho perde o par rótulo/valor no caminho. Com largura fixa, o cartão fica igual em
     qualquer monitor e o espaço extra vira MAIS COLUNAS, que é o que ajuda a comparar.
     A última linha não estica sozinha porque não há 1fr. */
  /* min(100%, 340px): em tela estreita a coluna encolhe junto com o container em vez de
     estourar. MEDIDO a 320 px: com 340px cravado, o cartão passava da tela e a página
     rolava de lado. Resolver por media query funcionaria só nas larguras previstas —
     assim vale em qualquer uma. */
  grid-template-columns:repeat(auto-fill, minmax(min(100%, 340px), 340px));
  justify-content:center;
}
/* MEDIDO em 12/08: estes 18 cartões (340×611) são as MAIORES cartas do site e não tinham
   elevação nenhuma — a única camada de `box-shadow` era a faixa de urgência, que é
   sinalização, não volume. Os levantamentos anteriores não os pegaram porque filtravam
   por `box-shadow !== none`, e eles tinham a faixa: **ter sombra não é o mesmo que ter
   elevação**. Agora a faixa e o relevo convivem, com as variáveis do sistema. */
.baz-card{
  display:flex; flex-direction:column; gap:.55rem;
  padding:.85rem .95rem; border-radius:var(--r-lg);
  background:var(--surface); border:1px solid var(--border);
  /* A faixa da esquerda é o canal de urgência: transparente por padrão, ganha
     cor quando o leilão está perto do fim. */
  box-shadow:inset 2px 0 0 transparent, var(--shadow-card), var(--panel-inset);
  cursor:pointer;
  transition:border-color .15s ease, box-shadow .18s ease;
}
/* Apontar levanta a carta — a mesma leitura do `.home-card`, que é a outra grade de
   cartas clicáveis do site. */
.baz-card:hover{
  border-color:color-mix(in oklch, var(--accent) 45%, var(--border));
  box-shadow:inset 2px 0 0 transparent, var(--shadow-soft), var(--panel-inset);
}
.baz-card:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.baz-card-topo{ display:flex; justify-content:space-between; align-items:flex-start; gap:.6rem; }
.baz-ident{ min-width:0; flex:1; }
.baz-nome-linha{ display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; }
.baz-nome{
  font-family:var(--font-display); font-size:1rem; font-weight:700; letter-spacing:.01em;
  color:var(--text); text-decoration:none;
}
.baz-nome:hover{ color:var(--accent-strong); }
.baz-externo{
  font-size:.8rem; color:var(--muted); text-decoration:none; line-height:1;
  padding:.1rem .2rem; border-radius:var(--r-sm);
}
.baz-externo:hover{ color:var(--accent-strong); background:var(--accent-soft); }
/* O relógio anda de segundo em segundo: sem largura tabular o texto ao lado dança. */
.baz-tempo-valor{ font-weight:700; color:var(--text); }
.baz-tempo{ text-align:right; font-size:.8rem; }

/* Caixas pareadas (servidor|pvp, fim|lance). O ganho é de varredura: o mesmo
   dado cai sempre na mesma posição, então comparar vinte cartões vira correr o
   olho por uma coluna. */
/* ── relevo das peças do cartão de leilão ──
   MEDIDO em 12/08 (levantamento das classes que ainda estavam sem `box-shadow`): o Radar
   concentra o maior bloco restante do site — `.baz-sk-valor` 108, `.baz-caixa` 72,
   `.baz-flag` 72 e `.baz-pvp` 18 = **270 peças**, todas planas dentro de cartões que já
   têm sombra, casas de inventário já escavadas e retratos já elevados.
   As três pastilhas usam os MESMOS valores das pastilhas do site (.pill/.voc-pill/
   .guild-tag) — é a mesma peça, muda só o nome da classe. A caixa é maior (151×46) e
   leva o contato um pouco mais aberto. */
.baz-sk-valor, .baz-flag, .baz-pvp{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 1px oklch(38% .06 250 / .18);
}
.baz-caixa{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .16);
}
html[data-theme="dark"] .baz-sk-valor,
html[data-theme="dark"] .baz-flag,
html[data-theme="dark"] .baz-pvp{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 2px oklch(0% 0 0 / .35);
}
html[data-theme="dark"] .baz-caixa{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .4);
}
.baz-caixas{ display:grid; grid-template-columns:1fr 1fr; gap:.4rem; }
.baz-caixa{
  display:flex; flex-direction:column; gap:.1rem; min-width:0;
  padding:.4rem .5rem; border-radius:var(--r-md);
  background:var(--surface-2); border:1px solid var(--border);
}
.baz-caixa-rot{
  font-family:var(--font-mono); font-size:.58rem; font-weight:500; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted);
}
.baz-caixa-val{
  font-weight:600; font-size:.85rem; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.baz-caixa-val a{ color:var(--text); text-decoration:none; }
.baz-caixa-val a:hover{ color:var(--accent-strong); }

/* Quatro caixas com o SPRITE de cada item em destaque, igual ao bazaar do RubinOT. O
   desenho comunica na hora o que o nome não comunica — dá pra ver que é uma armadura
   vermelha sem ler "sanguine breastplate". As vazias ficam reservadas pra que todo
   cartão tenha a mesma altura — lista com cartão de altura variável cansa. */
.baz-slots{ display:grid; grid-template-columns:repeat(4, 1fr); gap:.3rem; }
/* Slot de item é uma casa de inventário — e casa de inventário é ESCAVADA. MEDIDO em
   12/08: os 29 slots do Radar estavam com `box-shadow: none`, então a grade lia como
   quadradinhos pintados em vez de encaixes. Sombra na aresta de cima, luz na de baixo:
   o inverso do relevo, que é o que faz um buraco parecer buraco. */
.baz-slot{
  position:relative;
  display:flex; align-items:center; justify-content:center;
  min-height:2.9rem; padding:.2rem; border-radius:var(--r-sm);
  background:var(--surface-2); border:1px solid var(--border);
  font-size:.6rem; line-height:1.15; overflow:hidden;
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .15),
    inset 0 -1px 0 oklch(100% 0 0 / .35);
}
html[data-theme="dark"] .baz-slot{
  box-shadow:
    inset 0 1px 3px oklch(0% 0 0 / .38),
    inset 0 -1px 0 oklch(100% 0 0 / .06);
}
/* image-rendering:pixelated porque é arte de pixel: o suavizado do navegador borra o
   desenho. Mesma escolha do sprite do personagem. */
/* O sprite não tem fundo próprio, então aqui `drop-shadow` segue o RECORTE do item (o
   canal alfa do PNG) e não o retângulo — é o item pousado no fundo da casa, com a sombra
   acompanhando o contorno do desenho. `box-shadow` faria a moldura quadrada de sempre. */
.baz-slot-img{
  width:32px; height:32px; object-fit:contain;
  image-rendering:pixelated; image-rendering:crisp-edges;
  filter:drop-shadow(0 1px 1px oklch(0% 0 0 / .38));
}
html[data-theme="dark"] .baz-slot-img{ filter:drop-shadow(0 1px 1.5px oklch(0% 0 0 / .6)); }
/* Reserva pra item sem sprite: o nome, cortado em duas linhas. */
.baz-slot-nome{
  color:var(--text); font-weight:600; text-align:center;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
/* Quantidade e tier ficam SOBRE o sprite, nos cantos, como no cliente do jogo — assim
   não roubam espaço do desenho. */
.baz-slot-qtd, .baz-slot-tier{
  position:absolute; font-style:normal; text-decoration:none;
  font-size:.58rem; font-weight:700; padding:0 .15rem; border-radius:3px;
  background:color-mix(in oklch, var(--bg) 82%, transparent);
  font-variant-numeric:tabular-nums;
}
.baz-slot-qtd{ right:.15rem; bottom:.1rem; color:var(--text); }
.baz-slot-tier{ left:.15rem; top:.1rem; color:var(--accent-strong); }
.baz-slot-vazio{ background:transparent; border-style:dashed; opacity:.4; }
/* Na ficha completa a grade é livre: quantas colunas couberem, em vez das 4 fixas do
   cartão. Com centenas de itens, coluna fixa desperdiçaria metade da largura do modal. */
.baz-slots-ficha{
  grid-template-columns:repeat(auto-fill, minmax(46px, 1fr));
  gap:.25rem; margin-top:.35rem;
}

/* Skills em duas colunas com barra: dá pra ver "esse é de escudo alto" sem ler
   número nenhum. */
.baz-sk-grade{ display:grid; grid-template-columns:1fr 1fr; gap:.25rem .5rem; }
.baz-sk{ display:flex; align-items:center; gap:.35rem; min-width:0; }
.baz-sk-valor{
  flex:none; min-width:1.9rem; text-align:center; font-family:var(--font-mono);
  font-size:.68rem; font-weight:700; padding:.08rem .25rem; border-radius:var(--r-sm);
  background:var(--surface-2); color:var(--muted); border:1px solid var(--border);
}
/* Verde a partir de 100 (onde a skill vira argumento de venda); acento acima de
   70. Cor É reforço: o número continua ali pra quem não distingue as duas. */
.baz-sk-bom{ color:var(--accent-strong); border-color:color-mix(in oklch, var(--accent) 35%, transparent); }
.baz-sk-forte{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 35%, transparent); }
.baz-sk-corpo{ min-width:0; flex:1; display:flex; flex-direction:column; gap:.12rem; }
.baz-sk-nome{ font-family:var(--font-mono); font-size:.6rem; letter-spacing:.04em; color:var(--muted); text-transform:uppercase; }
/* MEDIDO em 13/08: são 108 destas numa ficha aberta (uma por skill), e eram as barras
   mais numerosas do site sem relevo nenhum — trilho e preenchimento ambos com
   `box-shadow: none`. É a MESMA metáfora que a barra de placar das rivalidades já usa
   desde o ciclo 45: um sulco escavado com uma faixa preenchendo por dentro.

   Os valores são metade dos de lá porque a peça é metade da altura (3 px contra 6,4 px):
   `inset 0 1px 1px` em vez de `0 1px 2px`, e a luz da borda de baixo sai — num trilho de
   3 px ela e a sombra de cima se sobrepõem e o sulco vira uma linha cinza sem direção. */
.baz-sk-barra{
  height:3px; border-radius:var(--r-pill); background:var(--surface-3); overflow:hidden;
  box-shadow:inset 0 1px 1px oklch(0% 0 0 / .16);
}
html[data-theme="dark"] .baz-sk-barra{ box-shadow:inset 0 1px 1px oklch(0% 0 0 / .34); }
/* O preenchimento leva as MESMAS duas camadas das outras barras do site (luz em cima,
   sombra embaixo) — quem muda com a altura é o sulco do trilho, não a faixa. Estas duas
   ficaram divergentes por um dia: a `.w-bar`, também de 3 px, tinha duas camadas aqui e
   esta tinha uma. */
.baz-sk-barra i{
  display:block; height:100%; background:var(--accent);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .3),
    inset 0 -1px 1px oklch(0% 0 0 / .22);
}
.baz-sk-forte + .baz-sk-corpo .baz-sk-barra i{ background:var(--online); }

.baz-ficha-resumo{
  display:grid; grid-template-columns:1fr 1fr; gap:.3rem .6rem;
  padding-top:.45rem; border-top:1px solid var(--border);
}
.baz-fic-col{ display:flex; flex-direction:column; gap:.15rem; min-width:0; }
.baz-fic-linha{ display:flex; justify-content:space-between; gap:.4rem; font-size:.7rem; align-items:center; }
.baz-fic-linha b{ color:var(--text); font-family:var(--font-mono); }
/* Rótulo com o ícone à esquerda, como no cartão do Exevo Pan. */
.baz-fic-rot{ display:inline-flex; align-items:center; gap:.3rem; min-width:0; }
/* Os SVGs foram copiados SEM width/height (o tamanho é responsabilidade daqui). Sem esta
   regra o navegador usa a caixa do viewBox e o ícone sai com mais de 100 px — medido. */
.baz-ico{
  width:14px; height:14px; flex:none;
  /* currentColor: o ícone acompanha a cor do texto ao lado, então serve os quatro temas
     claro/escuro sem nenhuma regra a mais. */
  fill:currentColor;
}
.baz-flag .baz-ico{ width:13px; height:13px; }

/* Linha que abre detalhe no hover/foco. O tracejado embaixo é o aviso de que ali tem
   mais coisa — sem ele, ninguém descobre que basta passar o mouse. */
.baz-tem-detalhe{
  cursor:help; border-radius:var(--r-sm); padding:.05rem .15rem; margin:0 -.15rem;
}
.baz-tem-detalhe .baz-fic-rot{ text-decoration:underline dotted; text-underline-offset:2px; }
.baz-tem-detalhe:hover, .baz-tem-detalhe:focus-visible{ background:var(--surface-2); outline:none; }
.baz-tem-detalhe:focus-visible{ box-shadow:inset 0 0 0 1px var(--accent); }
.baz-ver{ color:var(--muted); font-weight:600; font-size:.66rem; }

/* Painel do detalhe. Fica no <body> e não dentro do cartão de propósito: dentro, o
   overflow do cartão o cortaria. */
.baz-detalhe{
  position:absolute; z-index:60; width:16rem; max-width:calc(100vw - 1rem);
  padding:.6rem .7rem; border-radius:var(--r-md);
  background:var(--surface); border:1px solid var(--border);
  box-shadow:0 8px 24px oklch(0 0 0 / .22);
  font-size:.75rem;
}
/* max-width continua valendo: em 375px o painel largo encolhe junto com a tela. */
.baz-detalhe-largo{ width:23rem; }
.baz-detalhe h5{
  margin:0 0 .4rem; font-size:.72rem; font-weight:700;
  letter-spacing:.04em; text-transform:uppercase; color:var(--muted);
}
.baz-detalhe p{ margin:.35rem 0 0; }
.baz-det-linhas{ display:flex; flex-direction:column; gap:.25rem; }
.baz-det-linha{ display:grid; grid-template-columns:4.5rem 1fr auto; align-items:center; gap:.4rem; }
/* Sem relevo aqui de propósito: esta barra é uma das cinco da regra agrupada que começa
   em `.ha-barra`. Ver a explicação no `.charm-alt-barra`. */
.baz-det-barra{ height:5px; border-radius:3px; background:var(--surface-2); overflow:hidden; }
.baz-det-barra i{ display:block; height:100%; background:var(--accent); }
.baz-det-linha b{ font-family:var(--font-mono); color:var(--text); }
.baz-det-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.3rem; }
.baz-det-lista li{ display:flex; flex-direction:column; }
.baz-det-lista b{ color:var(--text); font-size:.74rem; }

/* Lista de quests: concluídas em destaque, pendentes apagadas — a mesma leitura que o
   RubinOT dá na aba Quests. Rola dentro do painel: são quase 20 linhas. */
.baz-det-resumo{ margin:0 0 .35rem !important; }
.baz-det-quests{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; gap:.1rem;
  max-height:15rem; overflow-y:auto;
}
.baz-det-quests li{ display:flex; align-items:center; gap:.35rem; font-size:.72rem; }
.baz-q-marca{ flex:none; width:.9rem; text-align:center; font-weight:700; }
.baz-q-ok{ color:var(--text); }
.baz-q-ok .baz-q-marca{ color:var(--online); }
/* Pendente fica apagada, não some: saber o que FALTA é metade da informação. */
.baz-q-nao{ color:var(--muted); opacity:.65; }

/* Conquistas: grau à esquerda, nome no meio, pontos à direita. A cor do grau é o que
   permite varrer a lista procurando o que é raro sem ler nome nenhum. */
.baz-det-conq{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; gap:.12rem;
  max-height:15rem; overflow-y:auto;
}
.baz-det-conq li{
  display:grid; grid-template-columns:3.6rem 1fr auto; align-items:center; gap:.35rem;
  font-size:.7rem; color:var(--muted);
}
.baz-c-grau{
  font-size:.58rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  padding:.05rem .25rem; border-radius:3px; text-align:center;
  background:var(--surface-2); border:1px solid var(--border);
}
.baz-c-nome{ color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.baz-det-conq b{ font-family:var(--font-mono); color:var(--muted); }
.baz-c-nome i{ font-style:normal; color:var(--accent); }
/* Raro pra cima ganha cor. Grau 4 existe uma só no jogo inteiro (Soul Mender). */
.baz-c-raro .baz-c-grau{ background:var(--accent-soft); color:var(--accent-strong); border-color:color-mix(in oklch, var(--accent) 45%, transparent); }
.baz-c-epico .baz-c-grau{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 45%, transparent); }
.baz-c-lendario .baz-c-grau{ background:var(--warn); color:var(--bg); border-color:var(--warn); }

/* Bestiário: nome à esquerda com espaço maior que o das gems (nome de criatura é longo:
   "Corrupted Draken Spellweaver"), barra proporcional e kills à direita. */
.baz-det-bicho{ grid-template-columns:7.5rem 1fr auto; }
.baz-det-bicho > span:first-child{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text);
}
/* Proficiência: sprite, nome, nível, barra de experiência relativa e o número. O sprite
   entra porque arma se reconhece pelo desenho antes do nome — e porque parte das armas
   ainda não tem nome no catálogo, e aí o desenho é a única identificação. */
.baz-det-armas{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; gap:.2rem;
  max-height:15rem; overflow-y:auto;
}
.baz-det-armas li{
  display:grid; grid-template-columns:auto 7rem 3rem 1fr auto; align-items:center; gap:.35rem;
  font-size:.7rem; color:var(--muted);
}
/* ── Estatísticas do bazaar ──────────────────────────────────────────────────────── */
.baz-estat-cabeca{ display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; margin-bottom:.5rem; }
.baz-estat-cabeca h3{ margin:0; }
.baz-periodo-toggles{ display:flex; gap:.3rem; margin-left:auto; }

.est-grafico{ overflow-x:auto; }
.est-svg{ width:100%; min-width:320px; height:auto; display:block; }
/* Cores por variável: o gráfico acompanha os quatro modos claro/escuro sem duplicar CSS. */
.est-grade{ stroke:var(--border); stroke-width:1; }
.est-eixo{ fill:var(--muted); font-size:11px; font-family:var(--font-mono); }
/* MEDIDO em 12/08: os gráficos do bazaar (esta área/linha e a rosca abaixo) não tinham
   `filter`, `defs` nem gradiente — desenho vetorial 100% chapado, curva e grade no mesmo
   plano. Aqui a curva passa a PAIRAR sobre a área e a grade.
   CORRIGIDO em 13/08: este comentário dizia "os dois únicos gráficos do site" e era falso
   ao ser escrito — havia mais três no perfil do personagem, invisíveis para a varredura
   porque não tinham classe. Estão em `.spark-*`, logo abaixo.
   ATENÇÃO à unidade: dentro de um SVG com viewBox, o drop-shadow é medido em unidades
   do viewBox, não em pixels de tela. Este gráfico é 720 de viewBox exibido a ~1220 px
   (escala 1,69×), então 1,2 aqui chega na tela como ~2 px. */
.est-linha{
  fill:none; stroke:var(--accent); stroke-width:2.5; stroke-linejoin:round; stroke-linecap:round;
  filter:drop-shadow(0 1.2px 1.2px oklch(38% .06 250 / .45));
}
.est-area{ fill:var(--accent-soft); stroke:none; }
/* Os pontos são contas pousadas sobre a curva: sombra curta, para descolarem dela sem
   virarem balões. */
.est-ponto{
  fill:var(--accent-strong); stroke:var(--surface); stroke-width:1.5;
  filter:drop-shadow(0 .9px .9px oklch(38% .06 250 / .5));
}
html[data-theme="dark"] .est-linha{ filter:drop-shadow(0 1.2px 1.6px oklch(0% 0 0 / .6)); }
html[data-theme="dark"] .est-ponto{ filter:drop-shadow(0 .9px 1.2px oklch(0% 0 0 / .65)); }
/* A unidade do viewBox corta nos dois sentidos: MEDIDO a 375 px, o gráfico desce para
   330 px de largura (escala 0,46×) e a sombra de 1,2 vira 0,5 px na tela — some. Nada
   quebra, mas o volume evapora justo onde a figura já é pequena. Os dois degraus abaixo
   compensam a escala para o efeito continuar valendo ~1,5 px em qualquer tela. */
@media (max-width: 700px){
  .est-linha{ filter:drop-shadow(0 2.2px 2.2px oklch(38% .06 250 / .45)); }
  .est-ponto{ filter:drop-shadow(0 1.6px 1.6px oklch(38% .06 250 / .5)); }
  html[data-theme="dark"] .est-linha{ filter:drop-shadow(0 2.2px 2.8px oklch(0% 0 0 / .6)); }
  html[data-theme="dark"] .est-ponto{ filter:drop-shadow(0 1.6px 2px oklch(0% 0 0 / .65)); }
}
@media (max-width: 430px){
  .est-linha{ filter:drop-shadow(0 3.2px 3.2px oklch(38% .06 250 / .45)); }
  .est-ponto{ filter:drop-shadow(0 2.4px 2.4px oklch(38% .06 250 / .5)); }
  html[data-theme="dark"] .est-linha{ filter:drop-shadow(0 3.2px 4px oklch(0% 0 0 / .6)); }
  html[data-theme="dark"] .est-ponto{ filter:drop-shadow(0 2.4px 3px oklch(0% 0 0 / .65)); }
}

/* ── as MINISSÉRIES do perfil (nível, exp, posição no ranking) ──
   MEDIDO em 13/08 na tela, abrindo cada aba: `filter: none` nos três e `stroke-linejoin:
   miter`. Eram os últimos gráficos chapados do site, e escaparam justamente por não terem
   classe — o comentário acima ainda diz "os dois únicos gráficos do site", o que era falso
   quando foi escrito. Ficam com o mesmo vocabulário do gráfico do bazaar: mesma paleta de
   sombra, ponto com sombra mais curta que a linha, e o ombro da curva arredondado.
   A escala aqui é OUTRA (viewBox 600 exibido a ~1263 px = 2,1x, contra 1,69x do bazaar),
   então os números são menores para chegar aos mesmos ~2 px na tela. */
.spark-linha{
  stroke-linejoin:round; stroke-linecap:round;
  filter:drop-shadow(0 1px 1px oklch(38% .06 250 / .45));
}
.spark-ponto{ filter:drop-shadow(0 .75px .75px oklch(38% .06 250 / .5)); }
html[data-theme="dark"] .spark-linha{ filter:drop-shadow(0 1px 1.3px oklch(0% 0 0 / .6)); }
html[data-theme="dark"] .spark-ponto{ filter:drop-shadow(0 .75px 1px oklch(0% 0 0 / .65)); }
/* Mesma compensação de escala do gráfico do bazaar: em tela estreita o SVG encolhe e a
   sombra encolhe junto, então o valor sobe para o efeito continuar valendo na tela. */
@media (max-width: 700px){
  .spark-linha{ filter:drop-shadow(0 1.8px 1.8px oklch(38% .06 250 / .45)); }
  .spark-ponto{ filter:drop-shadow(0 1.35px 1.35px oklch(38% .06 250 / .5)); }
  html[data-theme="dark"] .spark-linha{ filter:drop-shadow(0 1.8px 2.3px oklch(0% 0 0 / .6)); }
  html[data-theme="dark"] .spark-ponto{ filter:drop-shadow(0 1.35px 1.7px oklch(0% 0 0 / .65)); }
}
@media (max-width: 430px){
  .spark-linha{ filter:drop-shadow(0 3.2px 3.2px oklch(38% .06 250 / .45)); }
  .spark-ponto{ filter:drop-shadow(0 2.4px 2.4px oklch(38% .06 250 / .5)); }
  html[data-theme="dark"] .spark-linha{ filter:drop-shadow(0 3.2px 4px oklch(0% 0 0 / .6)); }
  html[data-theme="dark"] .spark-ponto{ filter:drop-shadow(0 2.4px 3px oklch(0% 0 0 / .65)); }
}

.est-vocacoes{ display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap; }
.est-rosca{ width:160px; height:160px; flex:none; }
/* A rosca é um anel de 22 de espessura: sem sombra, as 5 fatias eram faixas de cor
   coladas no papel. Com a sombra em CADA fatia, uma projeta sobre a vizinha e o anel
   ganha espessura — é o que faz um donut parecer um objeto e não um diagrama.
   Aqui o viewBox (160) bate com o tamanho na tela (160 px), então 1 unidade = 1 px. */
.est-fatia{
  fill:none; stroke-width:22;
  filter:drop-shadow(0 1.5px 2px oklch(38% .06 250 / .45));
}
html[data-theme="dark"] .est-fatia{ filter:drop-shadow(0 1.5px 2.5px oklch(0% 0 0 / .6)); }
.est-legenda{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.25rem; font-size:.78rem; }
.est-legenda li{ display:flex; align-items:center; gap:.4rem; }
.est-legenda b{ font-family:var(--font-mono); }
.est-cor-bolinha{ width:.6rem; height:.6rem; border-radius:2px; flex:none; }
/* Cinco cores distinguíveis, uma por vocação. Tons separados em matiz E em luminosidade:
   quem não distingue vermelho de verde ainda separa as fatias pelo claro/escuro. */
.est-cor-0{ stroke:oklch(0.62 0.16 250); background:oklch(0.62 0.16 250); }
.est-cor-1{ stroke:oklch(0.78 0.15 85);  background:oklch(0.78 0.15 85); }
.est-cor-2{ stroke:oklch(0.55 0.13 165); background:oklch(0.55 0.13 165); }
.est-cor-3{ stroke:oklch(0.70 0.17 25);  background:oklch(0.70 0.17 25); }
.est-cor-4{ stroke:oklch(0.48 0.12 305); background:oklch(0.48 0.12 305); }

.est-rankings{ display:flex; flex-direction:column; gap:1rem; }
.est-top h3{ margin:0 0 .5rem; }
.est-top .baz-estat-tabela th[scope="row"]{ color:var(--muted); font-family:var(--font-mono); }
.est-top .baz-estat-tabela td:first-of-type{ text-align:left; }

/* Painel de filtros dobrado. Mesmo tratamento do "Como está o mercado" logo abaixo —
   dois blocos recolhíveis seguidos precisam parecer a mesma coisa, senão a página vira
   uma coleção de controles que se comportam de jeitos diferentes. */
.baz-filtros-resumo{
  display:flex; align-items:baseline; gap:.6rem; flex-wrap:wrap;
  cursor:pointer; font-weight:600; list-style:none;
}
.baz-filtros-resumo::-webkit-details-marker{ display:none; }
.baz-filtros-resumo::after{ content:'▾'; margin-left:auto; color:var(--muted); }
.baz-filtros-painel[open] .baz-filtros-resumo::after{ content:'▴'; }
/* Aberto, o resumo vira cabeçalho do painel e precisa de respiro do que vem abaixo. */
.baz-filtros-painel[open] .baz-filtros-resumo{ margin-bottom:.7rem; }

/* Retrato do mercado. Fechado por padrão, então o summary precisa parecer clicável. */
.baz-estat-painel > summary{
  display:flex; align-items:baseline; gap:.6rem; flex-wrap:wrap;
  cursor:pointer; font-weight:600; list-style:none;
}
.baz-estat-painel > summary::-webkit-details-marker{ display:none; }
.baz-estat-painel > summary::after{ content:'▾'; margin-left:auto; color:var(--muted); }
.baz-estat-painel[open] > summary::after{ content:'▴'; }
.baz-estat-corpo{ margin-top:.7rem; }
.baz-estat-tit{ margin:.9rem 0 .35rem; font-size:.78rem; }
/* A tabela tem 6 colunas de número: no celular ela rola dentro do próprio bloco, e não
   empurra a página inteira pro lado. */
.baz-estat-rolagem{ overflow-x:auto; }
/* Mortes recentes da guilda: 25 linhas fazem o painel passar de 1.000 px, contra ~230 px
   dos vizinhos do mesmo grid — MEDIDO em 375 px, e é o mesmo desequilíbrio das tarefas
   #67/#68. A lista rola por dentro em vez de esticar o cartão.
   (Não dá pra reusar .baz-estat-rolagem: aquela só tem overflow-x, não limita altura.) */
.gp-mortes-lista{ max-height:15rem; overflow-y:auto; overflow-x:hidden; }
.gp-mortes-lista .voc-row{ gap:.4rem; }
.baz-estat-tabela{ width:100%; border-collapse:collapse; font-size:.74rem; }
.baz-estat-tabela th, .baz-estat-tabela td{
  padding:.3rem .5rem; text-align:right; white-space:nowrap;
  border-bottom:1px solid var(--border);
}
.baz-estat-tabela thead th{
  text-align:right; font-size:.64rem; text-transform:uppercase; letter-spacing:.04em;
  color:var(--muted); font-weight:700;
}
.baz-estat-tabela th[scope="row"], .baz-estat-tabela thead th:first-child{ text-align:left; }
.baz-estat-tabela tbody th{ font-weight:600; color:var(--text); }
.baz-estat-tabela td{ font-family:var(--font-mono); }
/* Faixa sem vendas suficientes pra afirmar preço: apagada, mas presente. Some-la
   esconderia que aquela faixa existe e teve movimento. */
.baz-estat-fraca{ opacity:.55; }

/* Asterisco no veredito: a régua veio do servidor inteiro porque o mundo do personagem
   não tinha vendas parecidas suficientes. O texto completo está no title da linha. */
/* Selo de "a comparação é do servidor, não deste mundo". Discreto para não competir com
   o veredito, mas legível — antes era um "*" que ninguém tinha como decifrar. */
.baz-vd-escopo{
  margin-left:.3rem; font-size:.66rem; color:var(--muted); opacity:.85; white-space:nowrap;
}

/* Hirelings: listas curtas de nome solto (5 cargos, 10 roupas) — chip cabe melhor que
   linha de tabela, que desperdiçaria a largura toda com um nome de uma palavra. */
.baz-det-sub{
  margin:.5rem 0 .25rem; font-size:.62rem; font-weight:700;
  letter-spacing:.04em; text-transform:uppercase; color:var(--muted);
}
.baz-chips{ display:flex; flex-wrap:wrap; gap:.2rem; }
.baz-chip{
  font-size:.66rem; padding:.1rem .35rem; border-radius:3px;
  background:var(--surface-2); border:1px solid var(--border); color:var(--text);
}

/* Anúncios anteriores: data, level, o que pediu e por quanto fechou. */
.baz-det-hist li{ grid-template-columns:4.5rem 4rem 1fr auto; }
.baz-det-hist b{ color:var(--muted); }
/* Só o que VENDEU ganha cor — é a linha que diz quanto o mercado realmente pagou. */
.baz-hist-vendeu b{ color:var(--online); font-weight:700; }
.baz-hist-aviso{
  margin:.3rem 0 !important; padding:.3rem .45rem; border-radius:4px;
  font-size:.68rem; line-height:1.3;
  background:var(--accent-soft); color:var(--accent-strong);
  border:1px solid color-mix(in oklch, var(--accent) 45%, transparent);
}
/* A linha no cartão fica em destaque: "já anunciado 8x" é informação que muda a decisão,
   não mais um número entre os outros. */
.baz-fic-alerta .baz-fic-rot{ color:var(--accent-strong); }
.baz-fic-alerta b{ color:var(--accent-strong); }

/* Bounty: três colunas (nome longo, nível, efeito) e sem barra — os quatro talismãs têm
   escalas de efeito diferentes entre si, então barra comparando um com o outro mentiria. */
.baz-det-bounty li{ grid-template-columns:1fr 3rem auto; }
.baz-det-bounty .baz-arma-nome{ white-space:normal; line-height:1.25; }

/* Battlepass: um bloco por temporada. Cada uma é um parágrafo curto, não uma linha de
   tabela — tem número, período e status, e espremer isso numa linha só obrigaria a
   abreviar tudo. */
.baz-det-bp{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; gap:.4rem;
  max-height:15rem; overflow-y:auto;
}
.baz-det-bp li{
  padding:.3rem .4rem; border-radius:4px;
  background:var(--surface-2); border:1px solid var(--border);
}
.baz-bp-topo{ display:flex; align-items:center; gap:.35rem; font-size:.72rem; }
.baz-bp-topo b{ color:var(--text); }
.baz-bp-pts{ margin-left:auto; font-family:var(--font-mono); color:var(--muted); font-size:.68rem; }
.baz-det-bp p{ margin:.15rem 0 0 !important; font-size:.66rem; line-height:1.35; }
/* Temporada com passe premium comprado ganha a cor de destaque, igual à mestria de arma
   e ao boss com os três níveis — é sempre o mesmo significado: "esta conta investiu". */
.baz-bp-premium .baz-arma-nivel{
  background:var(--accent-soft); color:var(--accent-strong);
  border-color:color-mix(in oklch, var(--accent) 45%, transparent);
}

/* Animus Masteries: só nomes, então vão em duas colunas — 85 nomes em coluna única
   viram uma tira estreita e comprida que ninguém varre. */
.baz-det-mastery{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:1fr 1fr; gap:.05rem .5rem;
  max-height:15rem; overflow-y:auto;
}
.baz-det-mastery li{
  font-size:.68rem; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* Bosstiary reusa a mesma linha, sem a coluna do sprite: nome de boss é longo
   ("The Enraged Thorn Knight") e merece o espaço que a arma dava à figura. */
.baz-det-bosses li{ grid-template-columns:11rem 2.4rem 1fr auto; }
.baz-arma-nome{ color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.baz-arma-nivel{
  font-family:var(--font-mono); font-size:.62rem; text-align:center;
  padding:.05rem .25rem; border-radius:3px;
  background:var(--surface-2); border:1px solid var(--border);
}
.baz-det-armas b{ font-family:var(--font-mono); color:var(--muted); }
/* Mestria é o teto da arma — merece destaque, é o que diferencia a conta. */
.baz-arma-mestre .baz-arma-nivel{
  background:var(--accent-soft); color:var(--accent-strong);
  border-color:color-mix(in oklch, var(--accent) 45%, transparent);
}

.baz-linha{ display:flex; justify-content:space-between; align-items:baseline; gap:.5rem; }
.baz-valor{ font-size:1.05rem; color:var(--muted); font-weight:700; }
/* Já tem lance = disputa em andamento, o dado mais quente do cartão. */
.baz-valor-lance{ color:var(--accent-strong); }
.baz-meta{ display:flex; flex-wrap:wrap; gap:.35rem; align-items:center; font-size:.78rem; }
.baz-skills{ display:flex; flex-wrap:wrap; gap:.3rem; }
.baz-skill{
  font-family:var(--font-mono); font-size:.68rem; padding:.1rem .4rem; border-radius:var(--r-pill);
  background:var(--surface-2); color:var(--muted); border:1px solid var(--border);
}
.baz-skill b{ color:var(--text); }
.baz-pontos{ margin-top:-.15rem; }
.baz-destaques{ display:flex; flex-wrap:wrap; gap:.3rem; }
.baz-tag{
  font-family:var(--font-mono); font-size:.62rem; font-weight:500; padding:.12rem .45rem; border-radius:var(--r-pill);
  background:var(--accent-soft); color:var(--accent);
  border:1px solid color-mix(in oklch, var(--accent) 30%, transparent);
}
.baz-tag-novo{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 35%, transparent); }
/* Urgência: acento abaixo de 6 h, âmbar abaixo de 1 h. Cor E texto mudam
   juntos — quem não distingue as cores continua lendo "12m 30s" no relógio. */
/* As duas variantes repetem o relevo porque só definir a faixa apagaria a elevação da
   base — e seriam justamente as cartas urgentes, as que mais precisam saltar, as únicas
   coladas no papel. Mesmo erro que a auditoria do ciclo 28 mapeou nos `:hover`. */
.baz-perto{ box-shadow:inset 2px 0 0 var(--accent), var(--shadow-card), var(--panel-inset); }
.baz-perto .baz-tempo-valor{ color:var(--accent-strong); }
.baz-urgente{ box-shadow:inset 2px 0 0 var(--warn), var(--shadow-card), var(--panel-inset); }
.baz-urgente .baz-tempo-valor{ color:var(--warn); }
.baz-perto:hover{ box-shadow:inset 2px 0 0 var(--accent), var(--shadow-soft), var(--panel-inset); }
.baz-urgente:hover{ box-shadow:inset 2px 0 0 var(--warn), var(--shadow-soft), var(--panel-inset); }
.baz-encerrado{ opacity:.55; }

/* Sprite do personagem (proxy /api/outfit). image-rendering:pixelated porque é
   arte de pixel: o suavizado do navegador borra o desenho. O RubinOT devolve um
   PNG 128x128 com o boneco em posição VARIÁVEL por roupa — por isso `contain` e
   nada de recorte, que decepava cabeça ou pés de uma parte dos personagens. */
/* O retrato do personagem no cartão do leilão. A sombra é `box-shadow` e NÃO
   `drop-shadow` de propósito: este elemento tem `background` próprio (a placa cinza atrás
   do boneco), então um drop-shadow seguiria o retângulo da placa e não o recorte do
   personagem — o mesmo truque que funciona no sprite do item aqui não teria efeito
   nenhum. Como placa, ela ganha o que placa ganha: bisel e sombra de contato. */
.baz-outfit{
  flex:none; width:64px; height:64px; object-fit:contain;
  image-rendering:pixelated;
  background:var(--surface-2); border-radius:var(--r-md);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 2px oklch(38% .06 250 / .28),
    0 4px 10px -6px oklch(38% .06 250 / .4);
}
html[data-theme="dark"] .baz-outfit{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 1px 3px oklch(0% 0 0 / .45),
    0 5px 12px -6px oklch(0% 0 0 / .55);
}
.baz-outfit-grande{ width:112px; height:112px; }
.baz-outfit-vazio{ display:block; background:var(--surface-2); border:1px dashed var(--border); }

/* ── ficha completa (modal) ── */
.baz-ficha-fundo{
  position:fixed; inset:0; z-index:60;
  display:flex; align-items:flex-start; justify-content:center;
  padding:2rem 1rem; overflow-y:auto;
  background:oklch(10% .02 258 / .66);
  backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
}
.baz-ficha{
  position:relative; width:min(720px, 100%);
  padding:1.15rem 1.25rem; border-radius:var(--r-lg);
  background:var(--surface); border:1px solid var(--border-strong);
  box-shadow:var(--shadow-flutuante);
  /* A ficha é a peça que mais flutua do site — véu escuro, desfoque no fundo e a sombra
     `--shadow-flutuante` — e era a ÚNICA que aparecia SECA (medido na tela:
     `animation-name: none`). O menu suspenso já resolveu esse mesmo problema no ciclo
     dele: "aparecia inteiro, do nada, sem dizer de onde veio".

     Aqui ela vem DE TRÁS, e isso não é enfeite: ela estava atrás do véu e passou à frente
     dele. Um objeto que se aproxima cresce, então nascer 7% menor e chegar ao tamanho
     é literalmente o movimento que a cena descreve. */
  animation:baz-ficha-entra .19s cubic-bezier(.2,.8,.3,1);
}
/* A perspectiva vai DENTRO do transform, e não no `.baz-ficha-fundo`, pelo mesmo motivo
   do menu: o fundo é `position:fixed; inset:0` (tela inteira) com `align-items:flex-start`,
   então uma perspectiva nele teria origem no centro da TELA, bem longe da ficha — que
   fica no topo. O resultado seria a ficha tombando de lado em vez de vir de frente. */
@keyframes baz-ficha-entra{
  from{ opacity:0; transform:perspective(900px) translateZ(-70px) translateY(10px); }
  to  { opacity:1; transform:perspective(900px) translateZ(0) translateY(0); }
}
/* O véu acompanha com um fade próprio: se ele aparecesse pronto enquanto a ficha ainda
   se aproxima, o escurecimento chegaria antes do objeto que o causa. */
.baz-ficha-fundo{ animation:baz-veu-entra .19s ease; }
@keyframes baz-veu-entra{ from{ opacity:0; } to{ opacity:1; } }
/* Quem pediu menos movimento recebe a ficha pronta. A sombra em camadas e o desfoque do
   fundo ficam: profundidade não é animação. */
@media (prefers-reduced-motion: reduce){
  .baz-ficha, .baz-ficha-fundo{ animation:none; }
}
.baz-ficha-fechar{ position:absolute; top:.6rem; right:.6rem; width:2rem; height:2rem; padding:0; line-height:1; }
.baz-ficha-topo{ display:flex; gap:.9rem; align-items:center; margin-bottom:.9rem; padding-right:2rem; }
.baz-ficha-topo h3{ margin:0; font-family:var(--font-display); font-size:1.15rem; }
.baz-ficha-topo p{ margin:.1rem 0 0; }
.baz-ficha-grade{ display:grid; grid-template-columns:repeat(auto-fit, minmax(120px, 1fr)); gap:.5rem; margin-bottom:.9rem; }
.baz-ficha-num{
  display:flex; flex-direction:column; gap:.05rem;
  padding:.45rem .55rem; border-radius:var(--r-md);
  background:var(--surface-2); border:1px solid var(--border);
}
.baz-ficha-num span{ font-family:var(--font-mono); font-size:.58rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
.baz-ficha-secao{ margin-bottom:.8rem; }
.baz-ficha-secao h4{ margin:0 0 .35rem; font-family:var(--font-mono); font-size:.66rem; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); font-weight:500; }
.baz-ficha-secao p{ margin:0; }
.baz-flags{ display:flex; flex-wrap:wrap; gap:.35rem; }
.baz-flag{
  display:inline-flex; align-items:center; gap:.25rem;
  font-family:var(--font-mono); font-size:.68rem; padding:.15rem .45rem; border-radius:var(--r-pill);
  background:var(--surface-2); color:var(--muted); border:1px solid var(--border);
}
/* Tem = verde; não tem = cinza. A diferença é o que decide compra. */
.baz-flag-sim{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 30%, transparent); }
/* Tipo de PvP: Optional é o mundo tranquilo (verde), Open é guerra aberta
   (âmbar), Retro é a regra antiga (neutro). */
.baz-pvp{
  font-family:var(--font-mono); font-size:.64rem; font-weight:500; padding:.1rem .4rem; border-radius:var(--r-pill);
  border:1px solid var(--border); background:var(--surface-2); color:var(--muted);
}
.baz-pvp-opcional{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 30%, transparent); }
.baz-pvp-aberto{ background:var(--warn-soft); color:var(--warn); border-color:color-mix(in oklch, var(--warn) 30%, transparent); }

/* Veredito de preço: "essa conta vale o que estão pedindo?". Fica logo abaixo
   do lance porque é a leitura DAQUELE número. */
.baz-avaliacao{ margin-top:-.35rem; align-items:center; }
.baz-vd-rotulo{ font-size:.74rem; font-weight:600; }
.baz-vd-barato .baz-vd-rotulo{ color:var(--online); }
.baz-vd-justo .baz-vd-rotulo{ color:var(--muted); }
.baz-vd-salgado .baz-vd-rotulo{ color:var(--accent-strong); }
.baz-vd-caro .baz-vd-rotulo{ color:var(--warn); }

/* Painel "me avise quando aparecer": entre os filtros e a lista de propósito —
   é a ação que se toma DEPOIS de procurar e não achar. */
.baz-alertas-painel{ margin:1rem 0 .5rem; }
.baz-alertas-corpo{ padding:.75rem .9rem; display:flex; flex-direction:column; gap:.6rem; }
.baz-alerta-form{ display:flex; flex-wrap:wrap; gap:.45rem; align-items:center; }
.baz-alerta-form input[type="text"]{ min-width:11rem; }
.baz-alerta-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.4rem; }
.baz-alerta-item{
  display:flex; justify-content:space-between; align-items:center; gap:.6rem;
  padding:.4rem .6rem; border-radius:var(--r-md);
  background:var(--surface-2); border:1px solid var(--border);
}

.baz-filtros .controls-right{ flex-wrap:wrap; row-gap:.5rem; }
.baz-item-busca{ min-width:13rem; }
.baz-num{ width:5.2rem; }
.baz-num-larga{ width:7rem; }
.baz-contagem{ margin:.2rem 0 0; font-family:var(--font-mono); font-size:.76rem; }

/* ── painel de filtros do bazaar ──
   Grupos com título em vez de uma fileira de menus suspensos: as opções de
   vocação e PvP ficam TODAS visíveis, e escolher é um clique. */
.baz-filtros-painel{ padding:.85rem .95rem; display:flex; flex-direction:column; gap:.75rem; }
.baz-filtros-barra{
  display:flex; flex-wrap:wrap; align-items:center; gap:.5rem;
  padding-bottom:.7rem; border-bottom:1px solid var(--border);
}
.baz-busca-nick{ flex:1; min-width:12rem; }
.baz-filtros-acoes{ display:flex; flex-wrap:wrap; align-items:center; gap:.4rem; margin-left:auto; }
.baz-filtros-grade{
  display:grid; gap:.75rem 1rem;
  grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));
}
.baz-grupo{ border:0; padding:0; margin:0; min-width:0; display:flex; flex-direction:column; gap:.35rem; }
.baz-grupo-largo{ grid-column:1 / -1; }
.baz-grupo legend{
  padding:0; font-family:var(--font-mono); font-size:.62rem; font-weight:500; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted);
}
.baz-toggles{ display:flex; flex-wrap:wrap; gap:.3rem; }
/* MEDIDO em 12/08 por auditoria (cruzar elementos com relevo contra os que têm `:active`):
   estes 16 são BUTTONs de verdade e eram os últimos botões do site sem relevo de tecla
   nem afundamento. O que havia era o anel `inset` do estado ligado — sinalização, não
   volume. Tecla que não afunda ao ser apertada é tecla falsa. */
.baz-toggle{
  font-size:.74rem; font-weight:600; padding:.28rem .6rem; border-radius:var(--r-pill);
  background:var(--surface-2); color:var(--muted);
  border:1px solid var(--border); cursor:pointer; font-family:var(--font-body);
  transition:border-color .15s ease, color .15s ease, box-shadow .15s ease;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .4),
    inset 0 -1px 0 oklch(0% 0 0 / .12),
    0 1px 1px oklch(38% .06 250 / .16);
}
html[data-theme="dark"] .baz-toggle{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    inset 0 -1px 0 oklch(0% 0 0 / .25),
    0 1px 2px oklch(0% 0 0 / .35);
}
.baz-toggle:hover{ border-color:color-mix(in oklch, var(--accent) 45%, var(--border)); color:var(--text); }
/* MEDIDO: só trocar o fundo pra --accent-soft não dava pra ver qual estava
   escolhido (quase a mesma cor do --surface-2 no tema claro). Como saber o que
   está filtrando é o ponto inteiro do painel, o estado ligado ganha um anel
   inset na cor de acento — não muda de tamanho ao acender. */
/* O anel do estado ligado SOMA com o relevo. Sozinho ele venceria por especificidade e
   o botão escolhido — justamente o que precisa se destacar — seria o único plano do
   grupo. É o mesmo erro que a auditoria do ciclo anterior mapeou nos `:hover`. */
.baz-toggle.ativo{
  background:var(--accent-soft); color:var(--accent-strong); font-weight:700;
  border-color:var(--accent);
  box-shadow:
    inset 0 0 0 1px var(--accent),
    inset 0 1px 0 oklch(100% 0 0 / .45),
    0 1px 2px oklch(38% .06 250 / .2);
}
html[data-theme="dark"] .baz-toggle.ativo{
  box-shadow:
    inset 0 0 0 1px var(--accent),
    inset 0 1px 0 oklch(100% 0 0 / .1),
    0 1px 3px oklch(0% 0 0 / .4);
}
/* Apertar afunda — vem DEPOIS do `.ativo` de propósito: as duas regras empatam em
   especificidade e quem estiver por último ganha, e ao apertar quem manda é o dedo. */
.baz-toggle:active{ box-shadow:inset 0 2px 3px oklch(0% 0 0 / .24); }
.baz-toggle:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
/* MEDIDO a 1350px: a coluna do grid dá 204px, mas "Preço" pedia 242px (dois
   campos de 7rem FIXOS). O excesso vazava do fieldset e caía por cima do grupo
   vizinho. flex:1 1 0 divide o espaço disponível e min-width:0 permite ficar
   menor que o conteúdo — sem ele o flex respeita o mínimo do input e o estouro
   volta. */
.baz-faixa{ display:flex; align-items:center; gap:.35rem; min-width:0; }
.baz-faixa .baz-num, .baz-faixa .baz-num-larga{ flex:1 1 0; width:auto; min-width:0; }
/* Rede de segurança: nenhum controle é mais largo que o próprio grupo. */
.baz-grupo input, .baz-grupo select{ max-width:100%; }
.baz-checks{ display:flex; flex-wrap:wrap; gap:.4rem; }
.baz-chip-check{
  display:inline-flex; align-items:center; gap:.35rem;
  font-size:.74rem; padding:.24rem .55rem; border-radius:var(--r-pill);
  background:var(--surface-2); border:1px solid var(--border); cursor:pointer;
}
.baz-chip-check:has(input:checked){
  background:var(--accent-soft); color:var(--accent-strong); font-weight:600;
  border-color:var(--accent); box-shadow:inset 0 0 0 1px var(--accent);
}
.baz-chip-check input{ margin:0; }
.baz-select{ max-width:100%; }
.baz-select-larga{ width:100%; }
.baz-filtros-mais{ border-top:1px solid var(--border); padding-top:.6rem; }
.baz-filtros-mais > summary{
  cursor:pointer; font-size:.78rem; font-weight:600; color:var(--muted);
  list-style:none; display:flex; align-items:center; gap:.35rem;
}
.baz-filtros-mais > summary::before{ content:'▸'; transition:transform .15s; }
.baz-filtros-mais[open] > summary::before{ transform:rotate(90deg); }
.baz-filtros-mais > summary:hover{ color:var(--text); }
.baz-filtros-mais > summary:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.baz-filtros-mais .baz-filtros-grade{ margin-top:.7rem; }

/* Comparador de guildas: selo de frescor ao lado do nome. O dado das guildas
   não acompanhadas é buscado no RubinOT na hora da visita. */
.cmp-selo{
  display:inline-block; vertical-align:middle; margin-left:.5rem;
  padding:.1rem .45rem; border-radius:var(--r-pill);
  font-family:var(--font-mono); font-size:.58rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  background:var(--surface-2); color:var(--muted); border:1px solid var(--border);
}
.cmp-selo-vivo{ background:var(--online-soft); color:var(--online); border-color:color-mix(in oklch, var(--online) 35%, transparent); }
.cmp-selo-alerta{ background:var(--accent-soft); color:var(--accent); border-color:color-mix(in oklch, var(--accent) 35%, transparent); }
.cmp-mundo{ margin:-.35rem 0 .6rem; }

/* ═══ otimizador de charms ═══
   Duas colunas: configuração à esquerda (personagem + os 9 charms), caçada à
   direita. A caçada é a coluna que cresce, então fica com a parte larga. */
/* Teto de largura porque esta página é feita de CARTÃO, não de tabela: numa tela de
   1920 o cartão do bicho chegava a 1370px e abria um vão de mais de 600px entre as
   resistências e o charm escolhido — os dois dados que a pessoa precisa ler juntos.
   O site já limita leitura em outros lugares (o .hero-sub tem max-width em ch). */
.charm-layout{
  display:grid; grid-template-columns:minmax(300px, 26rem) minmax(0, 1fr);
  gap:1rem; padding:1.2rem 1.8rem 3rem; align-items:start;
  max-width:1500px; margin-inline:auto;
}
.charm-col{ display:flex; flex-direction:column; gap:1rem; min-width:0; }
.charm-panel-hunt{ display:flex; flex-direction:column; }

/* Três campos: vida, mana e level. auto-fit em vez de 3 colunas fixas porque a
   coluna de configuração encolhe bastante antes do breakpoint do celular. */
.charm-char-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(6.5rem, 1fr)); gap:.8rem; }
.charm-char-grid label{
  display:block; font-family:var(--font-mono); font-size:.66rem; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.25rem;
}
.charm-char-grid input{
  width:100%; padding:.5rem .6rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-family:var(--font-mono); font-size:.88rem;
}
.charm-hint{ margin:.7rem 0 0; line-height:1.5; }
/* Aviso de dado faltando. Cor de atenção, não de erro: nada está quebrado, só
   incompleto — e a própria frase diz o que preencher. */
.charm-aviso{
  margin:.8rem 0 0; padding:.5rem .65rem; font-size:.78rem; line-height:1.5;
  border:1px solid color-mix(in oklch, var(--warn) 45%, transparent);
  border-left-width:3px; border-radius:var(--r-md);
  background:var(--warn-soft); color:var(--text);
}
.charm-aviso[hidden]{ display:none; }

.btn-mini{ padding:.25rem .55rem; font-size:.63rem; letter-spacing:.06em; }
/* Empurra o botão "Nova sessão" para a direita, deixando a contagem colada no
   título — ela pertence ao "Seus charms", não à ação. */
/* Contadores colam no título; o que é ação (Nova sessão, Ordem) vai para a direita.
   Sem isto o space-between do .panel-header joga a contagem para o meio do vão. */
#charm-conta, #charm-hunt-count{ margin-right:auto; font-family:var(--font-mono); }

.charm-ordem{ display:inline-flex; align-items:center; gap:.35rem; }
.charm-ordem[hidden]{ display:none; }
.charm-ordem select{
  border:1px solid var(--border); border-radius:var(--r-sm); background:var(--surface);
  color:var(--text); font-family:var(--font-body); font-size:.74rem; padding:.2rem .3rem; cursor:pointer;
}
.charm-bulk{ display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; margin-bottom:.7rem; }

.charm-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.35rem; }
/* MEDIDO em 12/08: os 21 cartões de charm e os 21 ícones dentro deles estavam com
   `box-shadow: none` e `filter: none` — lista chapada. Mesmo par do Radar do Bazaar:
   a peça pousa (bisel em cima, contato embaixo) e o desenho pousa dentro dela. */
.charm-item{
  display:flex; align-items:center; gap:.6rem; padding:.4rem .55rem;
  border:1px solid var(--border); border-radius:var(--r-md); background:var(--surface);
  transition:border-color .15s ease, opacity .15s ease, box-shadow .18s ease;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .16);
}
html[data-theme="dark"] .charm-item{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .38);
}
.charm-item.is-off{ opacity:.45; filter:grayscale(1); }

/* ── volume nos itens de charm ──
   Medido: os 21 itens já tinham relevo de SUPERFÍCIE (bisel em cima, contato embaixo, e o
   ícone com `drop-shadow`), mas `transform-style: flat` e `perspective: none` — a peça
   pousava e o conteúdo dela não tinha altura nenhuma.

   O ícone é o que vem à frente porque ele É o charm: numa lista de 21 nomes parecidos, o
   desenho é o que a pessoa reconhece primeiro. */
.tem-3d .charm-list{
  perspective:200vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .charm-item{
  transform-style:preserve-3d;
  will-change:transform;
  transition:border-color .15s ease, opacity .15s ease, box-shadow .18s ease,
             transform .25s cubic-bezier(.22,.61,.36,1);
}
.tem-3d .charm-item:hover{
  border-color:color-mix(in oklch, var(--accent) 38%, var(--border));
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 2px 4px oklch(38% .06 250 / .16),
    0 10px 20px -12px oklch(38% .06 250 / .32);
}
.tem-giro .charm-item:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }
html[data-theme="dark"] .tem-3d .charm-item:hover{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 2px 5px oklch(0% 0 0 / .4),
    0 12px 24px -14px oklch(0% 0 0 / .58);
}
/* Invólucro no meio da cadeia — quarta vez que isto aparece. Sem ele, o nome fica achatado
   mesmo com o `translateZ` escrito. */
.tem-3d .charm-item-info{ transform-style:preserve-3d; }
.tem-3d .charm-icon{ transform:translateZ(18px); }
.tem-3d .charm-name{ transform:translateZ(10px); }
/* Os tiers ficam quase no chão de propósito: o grupo é um sulco escavado (tem `inset` e
   `overflow:hidden`), e sulco que levanta deixa de ser sulco — mesma leitura da barra de
   placar das rivalidades. */
.tem-3d .charm-tiers{ transform:translateZ(2px); }

/* Item desligado NÃO ganha profundidade, e isso é de graça: o `filter: grayscale(1)` do
   `.is-off` já força `transform-style: flat` por ser grouping property. O acaso combina
   com a intenção — o que está fora da conta fica plano e cinza. */

/* Luz do ponteiro, igual às outras seis famílias. Não é enfeite acrescentado por simetria:
   o `.charm-item` já estava na lista de alvos do shared.js, então o JS vinha ESCREVENDO
   `--luz-x` e `--luz-y` neste elemento a cada movimento do ponteiro — e MEDIDO, nenhum
   pseudo-elemento lia. A conta era feita e jogada fora. */
.tem-3d .charm-item{ position:relative; }
.tem-3d .charm-item::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .2s ease; z-index:0;
  background:radial-gradient(circle at var(--luz-x, 50%) var(--luz-y, 50%),
    color-mix(in oklch,
      color-mix(in oklch, oklch(100% 0 0) var(--luz-branco, 0%), var(--accent))
      var(--luz-alfa-card, 22%), transparent),
    transparent 60%);
}
.tem-3d .charm-item:hover::before{ opacity:1; }
/* O conteúdo sobe acima do brilho: o ícone e os botões de tier são o que se lê e se
   clica, e um véu por cima deles apagaria justamente o que a luz deveria destacar. */
.tem-3d .charm-item > *{ position:relative; z-index:1; }

@media (prefers-reduced-motion: reduce){
  /* Só o giro sai; as camadas em translateZ ficam (ver o bloco do .summary, linha ~476). */
  .tem-3d .charm-item, .tem-3d .charm-item:hover{ transform:none; transition:none; }
}
/* O ícone NÃO tem fundo próprio (medido: backgroundColor transparente), então aqui
   `drop-shadow` acompanha o recorte do sprite em vez do retângulo — a mesma distinção
   que separou o sprite do item do retrato do personagem no Bazaar. */
.charm-icon{
  width:32px; height:32px; object-fit:contain; flex:0 0 auto; image-rendering:pixelated;
  filter:drop-shadow(0 1px 1px oklch(0% 0 0 / .38));
}
html[data-theme="dark"] .charm-icon{ filter:drop-shadow(0 1px 1.5px oklch(0% 0 0 / .6)); }
.charm-item-info{ min-width:0; flex:1; display:flex; flex-direction:column; gap:.1rem; }
.charm-name{ font-family:var(--font-display); font-size:.86rem; font-weight:600; letter-spacing:.01em; }
.charm-item-linha{ display:flex; align-items:center; gap:.4rem; min-width:0; }
.charm-toggle{ display:flex; align-items:center; gap:.3rem; font-size:.68rem; color:var(--muted); cursor:pointer; flex:0 0 auto; }
.charm-toggle input{ accent-color:var(--accent); width:.85rem; height:.85rem; }
/* De onde sai o dano do charm — informação, não controle. Por isso vem depois de um
   separador e em tom mais apagado que o rótulo do checkbox. */
.charm-item-fonte{
  font-size:.64rem; color:var(--muted); opacity:.75; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.charm-item-fonte::before{ content:"· "; }

/* Seletor T1/T2/T3: MEDIDO em 12/08 — 21 grupos, 63 botões, calha e pastilhas todas com
   `box-shadow: none`. É a MESMA anatomia das abas do ciclo 10 (uma calha com uma pastilha
   correndo dentro), então recebe o mesmo vocabulário: calha escavada, escolhido em relevo.

   Uma diferença obriga a adaptar: a calha tem `overflow:hidden` e as pastilhas colam nas
   bordas dela. Sombra PROJETADA do filho seria cortada pelo pai, então aqui o relevo é
   só `inset` — sem a camada de contato que as abas usam. */
.charm-tiers{
  display:flex; border:1px solid var(--border); border-radius:var(--r-sm); overflow:hidden; flex:0 0 auto;
  box-shadow:inset 0 1px 2px oklch(0% 0 0 / .14);
}
html[data-theme="dark"] .charm-tiers{ box-shadow:inset 0 2px 3px oklch(0% 0 0 / .34); }
.charm-tier{
  border:none; background:transparent; color:var(--muted); cursor:pointer;
  font-family:var(--font-mono); font-size:.66rem; font-weight:700; padding:.35rem .5rem;
  transition:background .15s ease, color .15s ease, box-shadow .15s ease;
}
/* `:not(.is-on)` e não só `:hover`: as duas regras empatam em especificidade e, sem o
   `:not`, apontar o tier ESCOLHIDO apagaria o relevo dele. */
.charm-tier:not(.is-on):hover{
  color:var(--text); background:var(--surface-2);
  box-shadow:inset 0 1px 0 oklch(100% 0 0 / .3);
}
.charm-tier.is-on{
  background:var(--accent-soft); color:var(--accent-strong);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .55),
    inset 0 -1px 0 oklch(0% 0 0 / .14);
}
html[data-theme="dark"] .charm-tier.is-on{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .12),
    inset 0 -1px 0 oklch(0% 0 0 / .3);
}
.charm-tier:active{ box-shadow:inset 0 2px 3px oklch(0% 0 0 / .22); }

/* A chave que põe os quatro de fora na disputa, e o perfil que eles exigem. */
.charm-extras-chave{
  margin-top:.8rem; padding:.6rem .7rem;
  border:1px solid var(--border); border-left:3px solid var(--accent);
  border-radius:var(--r-md); background:color-mix(in oklch, var(--accent-soft) 35%, transparent);
}
.charm-extras-chave p{ margin:.3rem 0 0; line-height:1.45; }
.charm-toggle-grande{ font-size:.82rem; color:var(--text); align-items:flex-start; }
.charm-toggle-grande input{ width:1rem; height:1rem; margin-top:.15rem; }

.charm-perfil{
  margin-top:.6rem; padding:.7rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface-2);
}
.charm-perfil[hidden]{ display:none; }
.charm-perfil-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(9rem, 1fr)); gap:.7rem; margin-bottom:.5rem; }
.charm-perfil-grid label{
  display:block; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.05em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.2rem; line-height:1.3;
}
.charm-perfil-grid input{
  width:100%; padding:.4rem .5rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-family:var(--font-mono); font-size:.85rem;
}
.charm-perfil-grid > div > .muted{ display:block; margin-top:.15rem; line-height:1.3; }
.charm-perfil-nota{ margin:.5rem 0 0 !important; line-height:1.5; }

/* Os quatro ficam visualmente distintos na lista: o que eles rendem é estimativa,
   não regra do jogo, e a diferença precisa aparecer. */
.charm-item.is-extra{ border-style:dashed; }
.charm-escolha.is-extra{
  border-style:dashed;
  background:color-mix(in oklch, var(--warn-soft) 40%, transparent);
  border-color:color-mix(in oklch, var(--warn) 45%, transparent);
}

/* A segunda caixa do cartão: o Minor daquela mesma criatura. Bolso diferente (echoes,
   não charm points), então cor diferente — a mesma que já marca os Minor na coluna da
   esquerda. Sem essa distinção os dois viram "dois charms" e a pessoa acha que precisa
   escolher entre eles, quando o jogo deixa equipar os dois na mesma criatura. */
.charm-escolha.is-minor{
  border-color:color-mix(in oklch, var(--voc-druid) 45%, var(--border));
  background:color-mix(in oklch, var(--voc-druid) 10%, transparent);
}
.charm-escolha.is-minor .charm-escolha-nome{ color:var(--voc-druid); }
.charm-escolha.is-minor.charm-escolha-vazia{
  border-style:dashed; border-color:var(--border); background:transparent;
}
/* O valor de um Minor é uma frase curta ("+2,4 Green Dragon Leather"), não um número
   de largura fixa como o dano/proc — sem isto ele estoura a caixa em telas médias. */
.charm-escolha.is-minor .charm-escolha-dano{ font-size:.82rem; line-height:1.25; }
.charm-escolha.is-minor .charm-escolha-pe{ line-height:1.35; }

/* ── Equipamento recomendado para a caçada ── */
.charm-equip{ margin-top:.9rem; padding:.7rem .8rem; border:1px solid var(--border); border-radius:var(--r-md); background:color-mix(in oklch, var(--surface-2) 45%, transparent); }
.charm-equip[hidden]{ display:none; }
.charm-equip-titulo{
  margin:0 0 .2rem; font-family:var(--font-display); font-size:.85rem; font-weight:700;
  letter-spacing:.02em; color:var(--accent-strong);
}
.charm-equip-perfil{ margin:0 0 .6rem !important; line-height:1.5; }
/* O boneco fica ao lado da lista em tela larga e acima dela no celular: são a MESMA
   informação em duas leituras — a de relance (o que equipar) e a detalhada (por quê). */
.charm-equip-corpo{ display:flex; gap:.9rem; align-items:flex-start; flex-wrap:wrap; }
.charm-equip-boneco{ flex:0 0 auto; }
.charm-equip-corpo .charm-equip-lista{ flex:1 1 22rem; min-width:0; }

/* Inventário: três colunas, na mesma disposição do cliente do jogo. */
.pd-grade{
  display:grid; grid-template-columns:repeat(3, 4.1rem); gap:.35rem;
  padding:.6rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:color-mix(in oklch, var(--surface-3) 55%, transparent);
}
.pd-slot{
  position:relative; width:4.1rem; height:4.1rem; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:.15rem; padding:0;
  border:1px solid var(--border); border-radius:var(--r-sm);
  background:var(--surface-2); cursor:pointer; color:inherit;
}
.pd-slot img{ width:32px; height:32px; object-fit:contain; image-rendering:pixelated; }
.pd-slot:hover, .pd-slot:focus-visible{ border-color:var(--accent); background:var(--accent-soft); }
.pd-slot.is-vazio, .pd-slot.is-fixo{ cursor:default; border-style:dashed; opacity:.55; }
.pd-vazio{ width:4.1rem; height:4.1rem; }
.pd-rotulo{
  font-family:var(--font-mono); font-size:.5rem; letter-spacing:.04em; text-transform:uppercase;
  color:var(--muted); text-align:center; line-height:1.15; padding:0 .15rem; overflow:hidden;
}
.pd-semsprite{ font-family:var(--font-mono); font-size:.8rem; font-weight:700; color:var(--muted); }
/* A munição que a aljava carrega: escondida até o mouse passar, e aí ela cobre a
   aljava. Mostrar as duas juntas o tempo todo não cabe em 4rem sem virar borrão. */
.pd-municao{
  position:absolute; inset:0; margin:auto; width:24px; height:24px;
  object-fit:contain; image-rendering:pixelated;
  opacity:0; transform:scale(.6); transition:opacity .12s ease, transform .12s ease;
  filter:drop-shadow(0 0 3px var(--surface));
}
.pd-slot.tem-municao:hover .pd-municao,
.pd-slot.tem-municao:focus-visible .pd-municao{ opacity:1; transform:scale(1.15); }
.pd-slot.tem-municao:hover .pd-item,
.pd-slot.tem-municao:focus-visible .pd-item{ opacity:.25; }
.pd-slot .pd-item{ transition:opacity .12s ease; }
/* Um ponto no canto avisa que existe algo escondido ali. */
.pd-slot.tem-municao::after{
  content:""; position:absolute; top:.25rem; right:.25rem; width:5px; height:5px;
  border-radius:50%; background:var(--accent);
}
.pd-slot.tem-municao:hover::after{ opacity:0; }

/* Bolinhas de imbuement, como no inventário. */
.pd-imbue{ display:flex; gap:.15rem; position:absolute; bottom:.2rem; }
/* Bolinha vazia = slot que a sugestão não conseguiu preencher. Fica oca, não some:
   o slot existe e a diferença entre "3 cheios" e "1 cheio de 3" é o que interessa. */
.pd-imbue i{ width:5px; height:5px; border-radius:50%; border:1px solid var(--accent); opacity:.5; }
.pd-imbue i.cheia{ background:var(--accent); opacity:.95; }

/* O que está DENTRO da peça, ao passar o mouse. Substitui o title nativo, que demora
   um segundo para abrir e não formata nada. */
.pd-slot{ position:relative; }
/* ── o tooltip da peça: fundo fora do sistema de temas ──
   Achado por auditoria de variáveis CSS (12/08): `--panel` era usada aqui e **não existe
   em lugar nenhum** — nem no CSS (52 variáveis declaradas), nem em elemento, nem no JS.
   Sobrava o fallback `#12161d`, um escuro FIXO, enquanto o texto seguia o tema.
   MEDIDO no tema claro: fundo com luminância 8 e texto com 23 → **15 pontos de
   contraste**, quase preto sobre quase preto. No escuro dava 85 e por isso nunca
   incomodou ninguém que testasse no escuro.
   Agora fundo e texto vêm do mesmo tema, e a sombra entra na família dos flutuantes
   (ciclo 29) em vez de um `rgba` fixo de uma camada. */
.pd-tip{
  position:absolute; bottom:calc(100% + .35rem); left:50%; transform:translateX(-50%);
  min-width:11rem; max-width:16rem; padding:.45rem .55rem; z-index:30;
  background:var(--surface); border:1px solid var(--border-strong); border-radius:.4rem;
  color:var(--text);
  box-shadow:var(--shadow-flutuante);
  display:none; text-align:left; font-size:.7rem; line-height:1.35; font-weight:400;
  white-space:normal; pointer-events:none;
}
/* :focus e não só :focus-visible — no celular não existe hover, e é o toque no botão
   que precisa abrir o conteúdo da peça. */
.pd-slot:hover .pd-tip, .pd-slot:focus .pd-tip{ display:block; }
.pd-tip strong{ display:block; margin-bottom:.15rem; }
.pd-tip-mun{ color:var(--muted); }
.pd-tip-imb{ list-style:none; margin:.2rem 0 0; padding:0; display:flex; flex-direction:column; gap:.1rem; }
.pd-tip-imb li{ color:var(--muted); }
/* O que ENTRA na conta fica destacado do que é só sugestão — a diferença é real e a
   pessoa precisa saber qual dos dois moveu o número. */
.pd-tip-imb li.pontua{ color:var(--accent); }
.pd-tip-imb li span{ opacity:.75; }
.pd-tip-imb li.vazio{ font-style:italic; opacity:.6; }
/* Sem esta legenda a cor é código secreto: o leitor vê dois tons e não sabe qual dos
   dois significa "está no número". */
.pd-tip-legenda{ display:block; margin-top:.25rem; font-size:.62rem; font-style:italic; opacity:.6; }
/* O material fica em linha própria, abaixo do imbuement a que pertence: na mesma linha
   o nome do imbuement sumia no meio de "20 Green Dragon Leathers, 10 Blazing Bones…". */
.pd-tip-astral{ display:block; font-size:.62rem; opacity:.55; margin-left:.35rem; }

/* Lista de compras do set. Fechada por padrão — é consulta, não decisão. */
.charm-equip-material{ margin-top:.4rem; }
.charm-equip-material summary{ cursor:pointer; font-size:.7rem; color:var(--muted); }
.charm-equip-material-lista{
  list-style:none; margin:.35rem 0 0; padding:0 0 0 .2rem;
  display:flex; flex-direction:column; gap:.15rem; font-size:.7rem;
}
.charm-equip-material-lista li span{ opacity:.6; font-size:.64rem; }
.pd-legenda{ margin:.35rem 0 0 !important; max-width:13rem; line-height:1.35; }

/* Barras dos dois painéis de análise (o que bate em você × o que o set defende). */
.charm-equip-analise{
  margin-top:.7rem; padding:.55rem .65rem; border:1px solid var(--border); border-radius:var(--r-sm);
  background:var(--surface);
}
.charm-equip-analise h4{
  margin:0 0 .4rem; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.07em;
  text-transform:uppercase; color:var(--muted); font-weight:600;
}
.charm-equip-analise p{ margin:.4rem 0 0 !important; line-height:1.45; }
.pd-barras, .pd-protecoes{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.25rem; font-size:.74rem; }
.pd-barras li, .pd-protecoes li{ display:flex; align-items:center; gap:.45rem; flex-wrap:wrap; }
.pd-barra-nome{ flex:0 0 4.2rem; font-weight:600; text-transform:capitalize; }
/* O relevo vem da regra agrupada das barras (a que começa em `.ha-barra`). */
.pd-barra{ flex:1 1 4rem; max-width:14rem; height:6px; background:var(--surface-3); border-radius:var(--r-pill); overflow:hidden; }
.pd-barra i{ display:block; height:100%; background:var(--bar-grad); border-radius:var(--r-pill); }
.pd-barra-num{ flex:0 0 2.2rem; text-align:right; font-family:var(--font-mono); font-variant-numeric:tabular-nums; font-weight:700; }
.pd-barra-prot{ font-size:.68rem; }
.pd-barra-prot.tem{ color:var(--online); }
.pd-barra-prot.falta{ color:var(--muted); }
/* Mundo fundido: o ranking abaixo está congelado no dia da fusão. Usa o par --warn/
   --warn-soft, que já responde ao tema claro e escuro, em vez de cor fixa. */
.rk-aviso-fundido{
  margin:0 0 .6rem; padding:.55rem .7rem; border-radius:.4rem;
  background:var(--warn-soft); color:var(--warn);
  border:1px solid color-mix(in oklch, var(--warn) 35%, transparent);
  font-size:.82rem; line-height:1.35;
}
.pd-util{ font-size:.68rem; color:var(--online); }
.pd-inutil{ font-size:.68rem; color:var(--muted); font-style:italic; }
/* Tipo de golpe que imbuement nenhum alcança (lifedrain, dreno de mana, físico). Fica
   ao lado do "sem resistência no set" para não convidar a gastar 250 mil à toa. */
.pd-barra-nope{ font-size:.68rem; color:var(--muted); font-style:italic; opacity:.85; }
@media (max-width:720px){
  .charm-equip-boneco{ margin:0 auto; }
  .pd-barra{ flex-basis:100%; max-width:none; order:3; }
}

.charm-equip-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.4rem; }
.charm-equip-linha{
  padding:.45rem .55rem; border:1px solid var(--border); border-radius:var(--r-sm);
  background:var(--surface);
}
/* A arma é a decisão que mais muda o resultado — e a única cujo número é o dano em
   si, e não um acréscimo. Ganha destaque para não se perder entre os oito slots. */
.charm-equip-linha.is-arma{ border-color:color-mix(in oklch, var(--accent) 45%, var(--border)); background:color-mix(in oklch, var(--accent-soft) 30%, transparent); }
.charm-equip-linha.is-vazia{ border-style:dashed; opacity:.75; display:flex; gap:.5rem; align-items:baseline; }
.charm-equip-topo{ display:flex; align-items:baseline; gap:.5rem; flex-wrap:wrap; }
.charm-equip-slot{
  font-family:var(--font-mono); font-size:.6rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); flex:0 0 6.5rem;
}
.charm-equip-nome{ font-weight:600; font-size:.82rem; }
.charm-equip-ganho{ margin-left:auto; font-family:var(--font-mono); font-variant-numeric:tabular-nums; font-weight:700; font-size:.82rem; }
.charm-equip-ganho .muted{ font-weight:500; font-size:.62rem; }
.charm-equip-attrs{ margin:.15rem 0 0 !important; line-height:1.45; }
.charm-equip-exige{ margin:.1rem 0 0 !important; opacity:.8; }
.charm-equip-detalhe > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.25rem;
  font-family:var(--font-mono); font-size:.6rem; letter-spacing:.05em; text-transform:uppercase;
  color:var(--muted); padding:.25rem 0; user-select:none;
}
.charm-equip-detalhe > summary::-webkit-details-marker{ display:none; }
.charm-equip-detalhe > summary::before{ content:"▸"; display:inline-block; transition:transform .15s ease; }
.charm-equip-detalhe[open] > summary::before{ transform:rotate(90deg); }
.charm-equip-detalhe > summary:hover{ color:var(--accent-strong); }
.charm-equip-porbicho, .charm-equip-alts{ list-style:none; margin:.15rem 0 .3rem; padding:0 0 0 .6rem; display:flex; flex-direction:column; gap:.12rem; font-size:.72rem; }
.charm-equip-porbicho li, .charm-equip-alts li{ position:relative; }
.charm-equip-porbicho li::before, .charm-equip-alts li::before{ content:"·"; position:absolute; left:-.5rem; color:var(--muted); }
.charm-equip-alt-nome{ font-weight:600; }
/* O motivo de a alternativa ter perdido um empate: precisa se distinguir do resto da
   linha, que é tudo cinza, senão some no meio dos números. */
.charm-equip-alt-motivo{ color:var(--warn, #d9a441); }
/* O que imbuir na peça é instrução de uso, não observação: fica destacado do resto
   das notas, que são todas cinza-itálico. */
.charm-equip-imbuir{ margin:.25rem 0 0 !important; color:var(--accent, #6fd3e0); }
/* Marca do que é sugerido mas NÃO entra no número exibido. Some no meio do texto de
   propósito — é ressalva, não alarme —, mas precisa existir. */
.charm-equip-fora{ opacity:.7; }
.charm-equip-fora-nota{ color:var(--muted); font-style:italic; }
.charm-equip-nota{ margin:.25rem 0 0 !important; font-style:italic; }
.charm-equip-resumo{ margin:.6rem 0 0 !important; line-height:1.5; padding-top:.5rem; border-top:1px solid var(--border); }
.charm-equip-ressalva{ margin:.4rem 0 0 !important; line-height:1.5; opacity:.85; }

/* ── TROCAR A PEÇA POR UMA DA LISTA "LOGO ATRÁS" ──
   A recomendação é uma ordem de compra, não um pacote: quase ninguém compra as oito peças
   de uma vez, e a de topo costuma ser a mais cara. Cada alternativa é um botão que remonta
   o set em volta dela. Por isso ela precisa PARECER clicável — a lista era texto cinza, e
   um alvo de clique que não se anuncia é um alvo que ninguém aperta. */
.charm-equip-alts li::before{ content:none; }
.charm-equip-troca{
  display:flex; align-items:baseline; gap:.4rem; flex-wrap:wrap; width:100%;
  text-align:left; font:inherit; color:inherit; cursor:pointer;
  padding:.2rem .4rem; border:1px solid transparent; border-radius:var(--r-sm);
  background:transparent; transition:background-color .12s ease, border-color .12s ease;
}
.charm-equip-troca:hover, .charm-equip-troca:focus-visible{
  background:color-mix(in oklch, var(--accent-soft) 60%, transparent);
  border-color:color-mix(in oklch, var(--accent) 40%, transparent);
  outline:none;
}
/* "usar esta" só aparece no alvo apontado: repetido em cinco linhas paradas, vira ruído e
   some justamente por estar em todas. */
.charm-equip-troca-acao{
  margin-left:auto; font-family:var(--font-mono); font-size:.6rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--accent-strong); opacity:0; transition:opacity .12s ease;
}
.charm-equip-troca:hover .charm-equip-troca-acao,
.charm-equip-troca:focus-visible .charm-equip-troca-acao{ opacity:1; }
/* O que a troca CUSTA em slots de imbuement, na própria linha da alternativa: é o dado que
   decide a compra, e o único que a pessoa não teria como adivinhar pelo preço. */
.charm-equip-alt-perde{ color:var(--warn); font-weight:600; }
.charm-equip-summary-troca{ color:var(--accent-strong); }

/* A peça escolhida pela pessoa. O cartão muda de cor porque ali o número deixou de ser
   "o melhor que existe" e passou a ser "o melhor com a sua peça dentro". */
.charm-equip-linha.is-trocado{ border-color:color-mix(in oklch, var(--accent) 55%, var(--border)); }
.charm-equip-selo{
  font-family:var(--font-mono); font-size:.58rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--accent-strong); border:1px solid color-mix(in oklch, var(--accent) 45%, transparent);
  border-radius:var(--r-pill); padding:.05rem .4rem;
}
.charm-equip-trocado{
  margin:.25rem 0 0 !important; font-size:.72rem; line-height:1.45; color:var(--muted);
  display:flex; align-items:baseline; gap:.4rem; flex-wrap:wrap;
}
.charm-equip-voltar{
  font:inherit; font-size:.68rem; cursor:pointer; color:var(--accent-strong);
  background:transparent; border:1px solid color-mix(in oklch, var(--accent) 40%, transparent);
  border-radius:var(--r-pill); padding:.1rem .5rem;
}
.charm-equip-voltar:hover{ background:color-mix(in oklch, var(--accent-soft) 70%, transparent); }

/* A faixa do alto. Junta o que está trocado (com um desfazer geral) e o que foi PEDIDO e
   não coube — este segundo precisa morar aqui, e não no cartão do slot, porque a peça que
   derruba um escudo é a ARMA, e é para a arma que a pessoa está olhando quando acontece. */
.charm-equip-trocas{ margin:.5rem 0 .2rem; display:flex; flex-direction:column; gap:.35rem; }
.charm-equip-trocas-lista, .charm-equip-trocas-recusa{
  margin:0 !important; font-size:.75rem; line-height:1.5; padding:.4rem .6rem;
  border-radius:var(--r-sm); border:1px solid var(--border);
}
.charm-equip-trocas-lista{
  background:color-mix(in oklch, var(--accent-soft) 45%, transparent);
  border-color:color-mix(in oklch, var(--accent) 35%, transparent); color:var(--text);
}
.charm-equip-trocas-recusa{
  background:var(--warn-soft); color:var(--warn);
  border-color:color-mix(in oklch, var(--warn) 35%, transparent);
}
.charm-equip-trocas-lista .charm-equip-voltar{ margin-left:.3rem; }

@media (max-width:720px){
  .charm-equip-slot{ flex:1 1 100%; }
  .charm-equip-ganho{ margin-left:0; }
  /* No celular não existe hover, então o "usar esta" nunca apareceria — e é ele que diz
     que a linha é clicável. Fica sempre visível, e sai da direita para não brigar com o
     nome da peça numa linha de 375px.
     `transition:none` não é enfeite: o fade existe para o hover, que aqui não acontece, e
     ele PRENDE o rótulo invisível quando a media query muda com o <details> fechado —
     transição não avança em conteúdo não renderizado, e ao abrir a lista o valor fica no
     inicial. Medido: opacity 0 com a regra mandando 1, e virava 1 na hora ao desligar a
     transição. Acontece ao girar o aparelho ou estreitar a janela. */
  .charm-equip-troca-acao{ opacity:1; margin-left:0; transition:none; }
}

/* Cabeçalho da segunda lista dentro do "Por que estes charms?". */
.charm-alt-titulo{
  margin:.7rem 0 0; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.07em;
  text-transform:uppercase; color:var(--voc-druid); font-weight:600;
}
.charm-alt-lista.is-minor{ border-color:color-mix(in oklch, var(--voc-druid) 28%, var(--border)); }
.charm-alt-lista.is-minor .charm-alt-barra i{ background:var(--voc-druid); }
/* Aqui o número traz a unidade junto ("+1,7 couros"), então precisa de espaço e não
   pode ser cortado como o valor numérico dos Major. */
.charm-alt-minor-valor{ min-width:8.5rem; font-size:.68rem; }

/* Os 4 Major Charms que não entram na conta de dano. */
.charm-sem-dano{ margin-top:.7rem; }
.charm-sem-dano > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.35rem;
  font-family:var(--font-mono); font-size:.64rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); padding:.2rem 0; user-select:none;
}
.charm-sem-dano > summary::-webkit-details-marker{ display:none; }
.charm-sem-dano > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.charm-sem-dano[open] > summary::before{ transform:rotate(90deg); }
.charm-sem-dano > summary:hover{ color:var(--accent-strong); }
.charm-sem-dano-conta{
  font-family:var(--font-mono); font-size:.58rem; color:var(--muted);
  background:var(--surface-3); border-radius:var(--r-pill); padding:.05rem .35rem;
}
.charm-sem-dano-lista{ list-style:none; margin:.4rem 0 0; padding:0; display:flex; flex-direction:column; gap:.5rem; }
.charm-sem-dano-lista li{ display:flex; gap:.5rem; align-items:flex-start; }
.charm-sem-dano-lista img{ width:24px; height:24px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; opacity:.75; margin-top:.1rem; }
.charm-sem-dano-lista p{ margin:0; line-height:1.45; }
.charm-sem-dano-nome{ font-family:var(--font-display); font-size:.8rem; font-weight:600; }
.charm-sem-dano-nome .muted{ font-family:var(--font-mono); font-size:.6rem; font-weight:500; }
.charm-sem-dano-motivo{ font-style:italic; opacity:.8; }

/* cor por elemento — mesma leitura do jogo, sem depender de ícone */
.el-physical{ color:var(--muted); } .el-fire{ color:var(--offline); }
.el-earth{ color:var(--online); } .el-ice{ color:var(--voc-druid); }
.el-energy{ color:var(--voc-sorcerer); } .el-death{ color:var(--text); }
.el-holy{ color:var(--warn); }

/* Faixa de desfazer: fundo de atenção porque algo sumiu da tela, e o botão é a
   saída. Fica acima da busca, no caminho do olho de quem acabou de agir. */
.charm-desfazer{
  display:flex; align-items:center; gap:.6rem; flex-wrap:wrap;
  margin-bottom:.7rem; padding:.45rem .6rem; font-size:.78rem; line-height:1.4;
  border:1px solid color-mix(in oklch, var(--warn) 45%, transparent); border-left-width:3px;
  border-radius:var(--r-md); background:var(--warn-soft); color:var(--text);
}
.charm-desfazer[hidden]{ display:none; }
.charm-desfazer span{ flex:1 1 12rem; min-width:0; }
.charm-desfazer .btn-ghost{ flex:0 0 auto; background:var(--surface); }

.charm-search{ position:relative; margin-bottom:.7rem; }
.charm-search input{
  width:100%; padding:.6rem .75rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-size:.88rem; font-family:var(--font-body);
}
.charm-search-results{
  position:absolute; left:0; right:0; top:calc(100% + 3px); z-index:20;
  max-height:20rem; overflow-y:auto; background:var(--surface);
  border:1px solid var(--border-strong); border-radius:var(--r-md); box-shadow:var(--shadow-flutuante);
}
.charm-search-results[hidden]{ display:none; }
.charm-busca-item{
  display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; cursor:pointer;
  padding:.4rem .6rem; border:none; border-bottom:1px solid var(--border); background:transparent; color:var(--text);
}
.charm-busca-item:last-child{ border-bottom:none; }
.charm-busca-item:hover, .charm-busca-item.is-marcado{ background:var(--accent-soft); }
.charm-busca-item img{ width:28px; height:28px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; }
.charm-busca-nome{ flex:1; min-width:0; font-size:.85rem; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.charm-busca-acao{ font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.08em; color:var(--accent); }
.charm-busca-vazia{ padding:.7rem; margin:0; text-align:center; }

.charm-presets{ display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; margin-bottom:.6rem; }

/* Caçadas salvas pelo usuário. O chip é um par de botões colados: abrir e apagar,
   com a borda em volta dos dois para ler como uma coisa só. */
.charm-exemplos > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.64rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); padding:.2rem 0; user-select:none;
}
.charm-exemplos > summary::-webkit-details-marker{ display:none; }
.charm-exemplos > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.charm-exemplos[open] > summary::before{ transform:rotate(90deg); }
.charm-exemplos > summary:hover{ color:var(--accent-strong); }
.charm-exemplos-chips{ display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; margin-top:.35rem; }

.charm-salvas{ display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; margin-bottom:.5rem; }
.charm-salvas:empty{ display:none; }
.charm-salva{
  display:inline-flex; align-items:stretch; border:1px solid var(--border);
  border-radius:var(--r-md); overflow:hidden; background:var(--surface-2);
}
.charm-salva:hover{ border-color:var(--accent); }
.charm-salva-abrir{
  border:none; background:transparent; color:var(--text); cursor:pointer;
  font-family:var(--font-display); font-weight:600; font-size:.72rem; letter-spacing:.02em;
  padding:.3rem .55rem; max-width:12rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.charm-salva-abrir .muted{ font-family:var(--font-mono); font-size:.62rem; }
.charm-salva-abrir:hover{ color:var(--accent-strong); }
.charm-salva-x{
  border:none; border-left:1px solid var(--border); background:transparent; color:var(--muted);
  cursor:pointer; padding:0 .4rem; font-size:.8rem; line-height:1;
}
.charm-salva-x:hover{ background:var(--offline); color:var(--on-accent); }

.charm-salvar-form{ margin:0 0 .6rem; }
.charm-salvar-form[hidden]{ display:none; }
.charm-salvar-form label{ display:block; margin-bottom:.2rem; }
.charm-salvar-linha{ display:flex; gap:.4rem; flex-wrap:wrap; }
.charm-salvar-linha input{
  flex:1 1 12rem; min-width:0; padding:.4rem .6rem; font-size:.82rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-family:var(--font-body);
}

/* Colar a lista do Hunt Analyser. Fechado por padrão: é o caminho de quem já tem os
   números na mão, não o primeiro passo de quem chega. */
.charm-colar{ margin-bottom:.9rem; }
.charm-colar > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.66rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--accent); padding:.2rem 0; user-select:none;
}
.charm-colar > summary::-webkit-details-marker{ display:none; }
.charm-colar > summary::before{ content:"▸"; font-size:.72rem; transition:transform .15s ease; display:inline-block; }
.charm-colar[open] > summary::before{ transform:rotate(90deg); }
.charm-colar > summary:hover{ color:var(--accent-strong); }
.charm-colar p{ margin:.4rem 0 .5rem; line-height:1.5; }
.charm-colar textarea{
  width:100%; resize:vertical; padding:.55rem .65rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text);
  font-family:var(--font-mono); font-size:.76rem; line-height:1.5;
}
.charm-colar-acoes{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; margin-top:.5rem; }
.charm-colar-eco{ margin:.5rem 0 0 !important; font-size:.76rem; color:var(--online); line-height:1.5; }
.charm-colar-eco.is-erro{ color:var(--offline); }
.charm-colar-eco[hidden]{ display:none; }

.charm-hunt{ display:flex; flex-direction:column; gap:.5rem; }
.charm-vazio{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:.3rem;
  padding:3rem 1rem; text-align:center; color:var(--muted);
}
.charm-vazio p{ margin:0; }
.charm-vazio-icone{ font-size:2rem; opacity:.5; }

/* flex-wrap é OBRIGATÓRIO aqui, não enfeite: o "Por que este charm?" pede a linha
   inteira (flex-basis:100%) e, sem wrap, ele disputava a MESMA linha com o resto e
   esmagava o nome e as resistências até uma letra por linha. Deu isso no ar. */
.charm-bicho{
  position:relative; display:flex; flex-wrap:wrap; align-items:center; gap:.5rem .8rem;
  padding:.6rem .7rem; border:1px solid var(--border); border-radius:var(--r-md); background:var(--surface);
}
.charm-bicho.sem-charm{ border-style:dashed; }
.charm-remove{
  position:absolute; left:-.45rem; top:-.45rem; width:1.15rem; height:1.15rem; line-height:1;
  border-radius:var(--r-pill); border:1px solid var(--border-strong); background:var(--surface-2);
  color:var(--muted); cursor:pointer; font-size:.8rem; display:flex; align-items:center; justify-content:center;
  padding:0; opacity:0; transition:opacity .15s ease, color .15s ease, background .15s ease;
}
/* :focus, não :focus-visible — um botão com opacity 0 que recebeu foco por QUALQUER
   caminho precisa aparecer, senão o teclado fica sobre um controle invisível. */
.charm-bicho:hover .charm-remove, .charm-remove:focus{ opacity:1; }
.charm-remove:hover{ background:var(--offline); color:var(--on-accent); border-color:var(--offline); }

.charm-bicho-fig{ display:flex; flex-direction:column; align-items:center; gap:.3rem; flex:0 0 auto; }
/* Os sprites vêm em 32×32 e 64×64 (e alguns tamanhos quebrados no meio). Caixa fixa
   com a imagem em tamanho natural até o limite: o de 32 fica 1:1 (nítido, sem
   borrão de upscale) e o de 64 desce suavizado, sem o serrilhado que `pixelated`
   causa ao REDUZIR. Por isso aqui não tem image-rendering — só nos ícones de charm,
   que são todos 32×32 e desenham 1:1. */
.charm-bicho-fig .charm-sprite{
  width:48px; height:48px; display:flex; align-items:center; justify-content:center; flex:0 0 auto;
}
.charm-bicho-fig .charm-sprite img{ max-width:100%; max-height:100%; width:auto; height:auto; }
.charm-qtd{ display:flex; align-items:center; border:1px solid var(--border); border-radius:var(--r-sm); overflow:hidden; }
.charm-qtd-btn{
  border:none; background:var(--surface-2); color:var(--muted); cursor:pointer;
  width:1.15rem; height:1.4rem; font-size:.75rem; line-height:1; padding:0;
}
.charm-qtd-btn:hover{ color:var(--accent-strong); background:var(--accent-soft); }
.charm-qtd input{
  width:2.1rem; border:none; text-align:center; background:var(--surface);
  color:var(--accent-strong); font-family:var(--font-mono); font-size:.7rem; font-weight:700;
  -moz-appearance:textfield; padding:.1rem 0;
}
.charm-qtd input::-webkit-outer-spin-button, .charm-qtd input::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
.charm-qtd input:focus{ outline:none; background:var(--accent-soft); }

/* Base de 14rem em vez de 0: com `flex:1` puro e min-width:0, este bloco podia
   encolher até sumir quando outro item pedia espaço demais. Agora ele tem chão.
   O teto existe pelo motivo oposto: sem ele o bloco crescia com a tela e jogava o
   charm escolhido para o outro canto do cartão, longe das resistências que explicam
   a escolha. As sete resistências cabem em uma linha bem antes de 34rem. */
.charm-bicho-info{ flex:1 1 14rem; min-width:0; max-width:34rem; }
.charm-bicho-info h3{ margin:0; font-family:var(--font-display); font-size:.92rem; font-weight:600; letter-spacing:.01em; }
.charm-bicho-info p{ margin:.05rem 0 .3rem; }
.charm-els{ display:flex; flex-wrap:wrap; gap:.2rem; }
.charm-el{
  font-family:var(--font-mono); font-size:.58rem; font-weight:700; letter-spacing:.02em;
  padding:.1rem .3rem; border-radius:var(--r-xs); border:1px solid var(--border); color:var(--muted);
}
.charm-el.fraco{ color:var(--online); border-color:color-mix(in oklch, var(--online) 40%, transparent); }
.charm-el.forte{ color:var(--offline); border-color:color-mix(in oklch, var(--offline) 30%, transparent); }
.charm-el.imune, .charm-el.cura{ opacity:.55; }
.charm-el.cura{ color:var(--offline); text-decoration:line-through; }
.charm-el.is-alvo{ background:var(--accent-soft); border-color:var(--accent); color:var(--accent-strong); }

/* As duas escolhas daquela criatura (o Major e o Minor) empilhadas numa coluna só.
   Lado a lado elas não cabem: o cartão tem ~730px e o bloco de informação já come a
   metade — a segunda caixa quebrava para a linha de baixo e o cartão dobrava de
   altura. Empilhadas, ocupam a mesma largura de uma. */
.charm-escolhas{
  display:flex; flex-direction:column; gap:.35rem;
  flex:0 1 auto; min-width:9.5rem; max-width:17rem;
}
.charm-escolhas .charm-escolha{ width:100%; }

.charm-escolha{
  flex:0 0 auto; min-width:9.5rem; text-align:center; padding:.4rem .5rem;
  border:1px solid color-mix(in oklch, var(--accent) 45%, var(--border)); border-radius:var(--r-md);
  background:color-mix(in oklch, var(--accent-soft) 45%, transparent);
}
.charm-escolha-top{ display:flex; align-items:center; justify-content:center; gap:.3rem; }
.charm-escolha-top img{ width:20px; height:20px; object-fit:contain; image-rendering:pixelated; }
.charm-escolha-nome{ font-family:var(--font-display); font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; }
.charm-escolha-tier{ font-family:var(--font-mono); font-size:.62rem; color:var(--muted); }
.charm-escolha-dano{ font-family:var(--font-mono); font-size:.95rem; font-weight:700; margin-top:.15rem; }
.charm-escolha-dano .muted{ font-size:.6rem; font-weight:500; }
.charm-escolha-pe{ margin-top:.05rem; }
.charm-escolha-vazia{
  display:flex; flex-direction:column; gap:.1rem; border-style:dashed; border-color:var(--border);
  background:transparent; color:var(--muted); font-size:.75rem;
}

/* "Por que este charm?" — linha própria, abaixo do resto do cartão. `flex:1 0 100%`
   com o wrap do .charm-bicho garante a quebra; sem o wrap, isto vira uma bomba. */
.charm-alt{ flex:1 0 100%; margin-top:.2rem; }
.charm-alt > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.63rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); padding:.25rem .1rem; user-select:none;
}
.charm-alt > summary::-webkit-details-marker{ display:none; }
.charm-alt > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.charm-alt[open] > summary::before{ transform:rotate(90deg); }
.charm-alt > summary:hover{ color:var(--accent-strong); }
.charm-alt-lista{
  list-style:none; margin:.3rem 0 0; padding:.5rem .6rem; display:flex; flex-direction:column; gap:.18rem;
  border:1px solid var(--border); border-radius:var(--r-md); background:color-mix(in oklch, var(--surface-2) 60%, transparent);
}
.charm-alt-linha{ display:flex; align-items:center; gap:.45rem; font-size:.72rem; }
.charm-alt-linha img{ width:16px; height:16px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; }
.charm-alt-nome{ flex:0 0 7.2rem; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.charm-alt-tier{ color:var(--muted); font-family:var(--font-mono); font-size:.62rem; font-weight:500; }
/* max-width porque a barra é uma COMPARAÇÃO, não um medidor: esticada por 600px ela
   empurra o número do dano para longe do nome do charm e obriga a varrer a linha
   inteira com o olho para ligar os dois. */
/* O relevo desta barra NÃO mora aqui: ela é uma das cinco da regra agrupada lá em cima
   (a que começa em `.ha-barra`), escrita em 10/08 justamente para as cinco terem o mesmo
   sulco num lugar só.

   Em 13/08 eu dupliquei o relevo aqui sem ter visto aquela regra — meu `grep` procurava
   por regras que COMEÇAM com `.charm-alt-barra{` e a agrupada começa com `.ha-barra,`.
   Como as duas têm a mesma especificidade e a minha vinha depois, ela vencia — e a minha
   dava só UMA camada ao preenchimento contra as DUAS da agrupada. Ou seja: eu tirei a
   sombra de baixo do preenchimento de duas barras achando que estava acrescentando. */
.charm-alt-barra{ flex:1 1 3rem; min-width:2rem; max-width:18rem; height:5px; background:var(--surface-3); border-radius:var(--r-pill); overflow:hidden; }
.charm-alt-barra i{ display:block; height:100%; background:var(--bar-grad); border-radius:var(--r-pill); }
.charm-alt-num{ flex:0 0 auto; min-width:3.2rem; text-align:right; font-family:var(--font-mono); font-variant-numeric:tabular-nums; }
.charm-alt-tag{
  flex:0 0 auto; font-family:var(--font-mono); font-size:.58rem; text-transform:uppercase; letter-spacing:.04em;
  color:var(--muted); border:1px solid var(--border); border-radius:var(--r-xs); padding:.05rem .25rem; white-space:nowrap;
}
.charm-alt-tag.escolhido{ color:var(--accent-strong); border-color:var(--accent); background:var(--accent-soft); }
.charm-alt-linha.is-escolhido .charm-alt-nome{ color:var(--accent-strong); }
.charm-alt-nota{ margin:.4rem 0 0; }

.charm-total{
  margin-top:1rem; padding-top:.8rem; border-top:1px solid var(--border);
}
.charm-total[hidden]{ display:none; }
/* Espelho do aviso, do lado do resultado. É botão porque leva ao campo que resolve. */
.charm-total-alerta{
  display:block; width:100%; margin-top:.6rem; text-align:left; cursor:pointer;
  padding:.45rem .6rem; font-size:.75rem; line-height:1.45; font-family:inherit;
  border:1px solid color-mix(in oklch, var(--warn) 45%, transparent); border-left-width:3px;
  border-radius:var(--r-md); background:var(--warn-soft); color:var(--text);
}
.charm-total-alerta:hover{ border-color:var(--warn); }
.charm-total-alerta::after{ content:" ↓"; color:var(--warn); font-weight:700; }
.charm-total-linha{ display:flex; justify-content:space-between; align-items:baseline; gap:.8rem; }
.charm-total-linha strong{ font-family:var(--font-mono); font-size:1.1rem; color:var(--accent-strong); }
.charm-total p{ margin:.3rem 0 0; line-height:1.5; }
/* O espaço entre um número em mono e a palavra seguinte some visualmente porque a
   mono é mais estreita que o corpo do texto — daí o respiro explícito. */
.charm-total-explica strong{ color:var(--text); font-family:var(--font-mono); margin-inline:.1em; }
/* Filete à esquerda: o balanço de charms é um recado sobre a montagem, não parte da
   explicação do número acima. */
.charm-total-balanco{
  border-left:2px solid var(--border-strong); padding-left:.5rem; margin-top:.45rem !important;
}
.charm-total-balanco strong{ color:var(--warn); font-family:var(--font-mono); }
/* "Onde investir pontos de charm" — mesma linguagem do painel por bicho. */
.charm-investir{ margin-top:.8rem; }
.charm-investir[hidden]{ display:none; }
.charm-investir > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.35rem;
  font-family:var(--font-mono); font-size:.66rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--accent); padding:.2rem 0; user-select:none;
}
.charm-investir > summary::-webkit-details-marker{ display:none; }
.charm-investir > summary::before{ content:"▸"; font-size:.72rem; transition:transform .15s ease; display:inline-block; }
.charm-investir[open] > summary::before{ transform:rotate(90deg); }
.charm-investir > summary:hover{ color:var(--accent-strong); }
.charm-investir-conta{
  font-family:var(--font-mono); font-size:.58rem; color:var(--accent-strong);
  background:var(--accent-soft); border-radius:var(--r-pill); padding:.05rem .35rem;
}
.charm-investir-lista{
  list-style:none; margin:.4rem 0 0; padding:.5rem .6rem; display:flex; flex-direction:column; gap:.2rem;
  border:1px solid var(--border); border-radius:var(--r-md); background:color-mix(in oklch, var(--surface-2) 60%, transparent);
}
.charm-investir-linha{ display:flex; align-items:center; gap:.45rem; font-size:.73rem; }
.charm-investir-linha img{ width:18px; height:18px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; }
.charm-investir-nome{ flex:0 0 6.6rem; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.charm-investir-acao{
  flex:0 0 auto; font-family:var(--font-mono); font-size:.6rem; text-transform:uppercase; letter-spacing:.04em;
  color:var(--muted); border:1px solid var(--border); border-radius:var(--r-xs); padding:.05rem .3rem; white-space:nowrap;
}
.charm-investir-ganho{
  flex:0 0 auto; min-width:3.4rem; text-align:right; font-family:var(--font-mono);
  font-variant-numeric:tabular-nums; font-weight:700; color:var(--online);
}
.charm-investir-pct{ flex:0 0 auto; min-width:3.2rem; text-align:right; font-family:var(--font-mono); font-size:.66rem; }
.charm-investir-nota{ margin:.45rem 0 0 !important; line-height:1.5; }
.charm-investir-detalhes{ display:block; margin-top:.15rem; opacity:.85; }

.charm-acoes{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; margin-top:.9rem; }
.charm-acoes[hidden]{ display:none; }
.charm-acoes-eco{ font-size:.74rem; color:var(--online); flex:1 1 12rem; min-width:0; }
.charm-acoes-eco.is-erro{ color:var(--offline); }
.charm-nota{ margin:1rem 0 0; line-height:1.6; }

/* ═══ divisor de loot ═══ */
.ls-layout{
  display:grid; grid-template-columns:minmax(300px, 30rem) minmax(0, 1fr);
  gap:1rem; padding:1.2rem 1.8rem 3rem; align-items:start;
  max-width:1500px; margin-inline:auto;
}
.ls-entrada{ padding:1.25rem 1.35rem; }
.ls-ajuda{ margin:0 0 .6rem; line-height:1.5; }
.ls-entrada textarea{
  width:100%; resize:vertical; padding:.6rem .7rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text);
  font-family:var(--font-mono); font-size:.74rem; line-height:1.55;
}
.ls-entrada-acoes{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; margin-top:.7rem; }
.ls-erro{
  margin:.7rem 0 0; padding:.5rem .65rem; font-size:.78rem; line-height:1.5;
  border:1px solid color-mix(in oklch, var(--warn) 45%, transparent); border-left-width:3px;
  border-radius:var(--r-md); background:var(--warn-soft); color:var(--text);
}
.ls-erro[hidden]{ display:none; }

.ls-saida{ display:flex; flex-direction:column; gap:1rem; min-width:0; }
.ls-saida[hidden]{ display:none; }
.ls-panel{ padding:1.25rem 1.35rem; }

.ls-transferencias{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.4rem; }
.ls-transf{
  display:flex; align-items:center; gap:.6rem; flex-wrap:wrap;
  padding:.5rem .65rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface-2); font-size:.86rem;
}
.ls-transf.ls-nada{ color:var(--muted); font-size:.82rem; background:transparent; border-style:dashed; }
.ls-de{ font-weight:600; }
.ls-seta{ color:var(--accent); font-weight:700; }
.ls-para{ font-weight:600; color:var(--accent-strong); }
.ls-valor{ margin-left:auto; font-family:var(--font-mono); font-variant-numeric:tabular-nums; font-weight:700; }

.ls-acoes{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; margin-top:.8rem; }
.ls-eco-inline{ font-size:.74rem; color:var(--online); flex:1 1 10rem; }
.ls-eco-inline.is-erro{ color:var(--offline); }
.ls-comandos{
  margin:.7rem 0 0; padding:.6rem .7rem; overflow-x:auto;
  border:1px solid var(--border); border-radius:var(--r-md); background:var(--surface-2);
  font-family:var(--font-mono); font-size:.76rem; line-height:1.6; color:var(--text);
}

.ls-totais{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(8rem, 1fr)); gap:.7rem; margin-bottom:.9rem;
}
.ls-totais > div{ display:flex; flex-direction:column; gap:.1rem; }
.ls-totais strong{ font-family:var(--font-mono); font-size:1rem; }
.ls-negativo{ color:var(--offline); }
.ls-tabela{ padding:0; overflow-x:auto; }
.ls-tabela table{ min-width:34rem; font-size:.82rem; }
.ls-nome{ font-weight:600; }
.ls-lider{
  margin-left:.35rem; font-family:var(--font-mono); font-size:.58rem; text-transform:uppercase;
  letter-spacing:.06em; color:var(--warn); border:1px solid color-mix(in oklch, var(--warn) 45%, transparent);
  border-radius:var(--r-xs); padding:.05rem .25rem;
}
.ls-ajuste{ font-family:var(--font-mono); font-size:.76rem; white-space:nowrap; }
.ls-ajuste.paga{ color:var(--offline); }
.ls-ajuste.recebe{ color:var(--online); }
.ls-ajuste.zerado{ color:var(--muted); }

/* ═══ mobile ═══ */
@media (max-width: 720px){
  .topbar{ padding:.7rem 1rem; gap:.5rem; grid-template-columns:1fr; }
  .topbar-brand{ grid-column:1; grid-row:1; justify-self:center; }
  .site-logo{ height:44px; }
  .health{ grid-column:1; grid-row:2; justify-self:center; font-size:.78rem; flex-wrap:wrap; }
  .hero{ padding:2rem 1rem .4rem; }
  .summary, .comparison, .insights{ grid-template-columns:1fr; padding-left:1rem; padding-right:1rem; }
  .watchlist, .controls{ padding-left:1rem; padding-right:1rem; }
  .table-wrap{ padding-left:1rem; padding-right:1rem; }
  .home-cards, .home-grid, .home-news-section, .home-favorites-section{ padding-left:1rem; padding-right:1rem; }
  .home-cards{ grid-template-columns:1fr; }
  .home-card:first-child{ grid-column:auto; }
  .site-footer{ padding-left:1rem; padding-right:1rem; }
  #watchlist-form{ width:100%; }
  #watchlist-form input{ flex:1 1 45%; min-width:8rem; }
  .controls{ flex-direction:column; align-items:stretch; }
  .controls-right{ justify-content:flex-start; }
  #search{ min-width:0; flex:1 1 auto; }
  .col-picker-menu{ right:auto; left:0; }
  table{ font-size:.8rem; }
  .countdown{ border-left:none; padding-left:0; margin-left:0; flex-basis:100%; }
  .global-search{ margin-left:0; width:100%; }
  .global-search input{ width:100%; }
  .global-search-results{ left:0; right:auto; }
  .stats-strip{ gap:1.2rem; }
  /* Charms: uma coluna só. O botão de remover fica sempre visível — no toque não
     existe hover, e sem ele não haveria como tirar um bicho da lista. */
  .charm-layout, .ls-layout{ grid-template-columns:1fr; padding-left:1rem; padding-right:1rem; }
  .ls-entrada-acoes button, .ls-acoes .btn-ghost{ min-height:40px; }
  .ls-valor{ margin-left:0; flex-basis:100%; }
  /* A caçada sobe para o topo. Em coluna única, a configuração (dois campos + NOVE
     charms) empurrava a busca de monstros para uns 800px de rolagem — e adicionar
     bicho é a primeira coisa que se quer fazer ao abrir a página. Os padrões
     (todos ligados, T2) já deixam a conta funcionando sem tocar em nada lá embaixo. */
  .charm-col-hunt{ order:1; }
  .charm-col-config{ order:2; }
  .charm-remove{ opacity:1; }
  /* Base menor que a do desktop: com os botões de quantidade em 2,2rem, uma base de
     14rem não cabia ao lado do sprite e o nome caía para a linha de baixo, esticando
     cada cartão sem motivo. Com 8rem ele senta ao lado e cresce até o fim da tela. */
  .charm-bicho-info{ flex:1 1 8rem; }
  .charm-escolha{ flex:1 1 100%; }
  /* A coluna das duas escolhas passa a ocupar a largura toda (o max-width de 17rem
     do desktop deixaria uma faixa vazia à direita no celular). */
  .charm-escolhas{ flex:1 1 100%; max-width:none; }
  /* Esta tela é feita de botão pequeno (tier, ±, remover). MEDIDO a 375px: 18 a 25px
     de altura — o mesmo erro já corrigido nos filtros do bazaar. 40px é o mínimo
     confortável pro dedo; o remover fica redondo de 30px porque é canto de cartão. */
  .charm-tier{ min-height:40px; min-width:2.6rem; }
  .btn-mini{ min-height:36px; padding-inline:.8rem; }
  /* Os .btn-ghost desta tela (nova sessão, montar caçada, copiar) e os <summary>
     clicáveis nascem com 21 a 29px — MEDIDO a 375px. Mesmo motivo dos botões acima. */
  .charm-layout .btn-ghost, .charm-acoes .btn-ghost{ min-height:40px; }
  .charm-colar > summary, .charm-alt > summary, .charm-exemplos > summary{ min-height:40px; }
  /* Chips das caçadas salvas: 25px de altura e o × com 21px de largura — o × é o
     alvo mais fácil de errar da tela, e errar nele apaga uma caçada. */
  .charm-salva-abrir{ min-height:40px; }
  .charm-salva-x{ min-width:2.5rem; font-size:1rem; }
  .charm-qtd-btn{ width:2.2rem; height:2.2rem; font-size:1rem; }
  .charm-qtd input{ width:2.6rem; height:2.2rem; font-size:.85rem; }
  .charm-remove{ width:1.9rem; height:1.9rem; font-size:1.1rem; left:-.6rem; top:-.6rem; }
  .charm-toggle{ padding:.25rem 0; }
  .charm-toggle input{ width:1.1rem; height:1.1rem; }

  /* MEDIDO a 375px: a linha do "Por que este charm?" tem nome + barra + número + a
     etiqueta do motivo, e "está em Frost Dragon" sozinha pede 129px. Somava 444px de
     conteúdo em 375px de tela e empurrava a PÁGINA INTEIRA para o lado.
     Deixar o flex quebrar sozinho resolveu o estouro e criou coisa pior: a segunda
     linha de um charm encostava no ícone do charm seguinte, e não dava para saber de
     quem era o número. Grid resolve os dois: cada charm ocupa um bloco de duas linhas
     com posições fixas, e um filete separa um do outro. */
  .charm-alt, .charm-investir{ min-width:0; }

  .charm-alt-linha{
    display:grid; grid-template-columns:16px minmax(0,1fr) auto;
    grid-template-areas: "ico nome num" "bar bar tag";
    gap:.15rem .4rem; align-items:center;
    padding-bottom:.3rem; border-bottom:1px solid var(--border);
  }
  .charm-alt-linha:last-child{ border-bottom:none; padding-bottom:0; }
  .charm-alt-linha img{ grid-area:ico; }
  .charm-alt-nome{ grid-area:nome; flex:none; }
  .charm-alt-num{ grid-area:num; }
  .charm-alt-barra{ grid-area:bar; max-width:none; }
  .charm-alt-tag{ grid-area:tag; flex:none; justify-self:end; }

  .charm-investir-linha{
    display:grid; grid-template-columns:18px minmax(0,1fr) auto;
    grid-template-areas: "ico nome ganho" ". acao pct";
    gap:.15rem .4rem; align-items:center;
    padding-bottom:.35rem; border-bottom:1px solid var(--border);
  }
  .charm-investir-linha:last-child{ border-bottom:none; padding-bottom:0; }
  .charm-investir-linha img{ grid-area:ico; }
  .charm-investir-nome{ grid-area:nome; flex:none; }
  .charm-investir-ganho{ grid-area:ganho; }
  .charm-investir-acao{ grid-area:acao; justify-self:start; }
  .charm-investir-pct{ grid-area:pct; }
  /* A barra sai: aqui o percentual ao lado já diz a proporção, e ela só roubaria
     uma terceira linha de cada item. */
  .charm-investir-linha .charm-alt-barra{ display:none; }
}
@media (max-width: 560px){
  /* Lado a lado não cabe: empilha em vez de sumir, pra o rótulo da página não se
     perder justamente onde há menos contexto visível (9 das 15 páginas não têm
     herói — o .eyebrow é a única coisa no topo que diz onde você está). */
  .topbar-brand{ flex-direction:column; gap:.3rem; }
  .eyebrow{ padding-left:0; border-left:none; }
}
@media (max-width:420px){
  /* No celular a grade de 4 slots e as caixas pareadas ficariam ilegíveis; uma
     coluna a menos resolve sem esconder nada. */
  .baz-slots{ grid-template-columns:repeat(2, 1fr); }
  .baz-sk-grade{ grid-template-columns:1fr; }
  .baz-filtros-acoes{ margin-left:0; }
  /* MEDIDO a 375px: os botões de filtro ficavam com 25px de altura — alvo
     pequeno demais pro dedo, e este painel é feito de botão. */
  .baz-toggle, .baz-chip-check{ min-height:40px; padding-inline:.75rem; }
}

/* Colar o Damage Input Analyser, dentro do perfil de combate. */
.charm-dano-colar{ margin-top:.6rem; }
.charm-dano-colar > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.63rem; letter-spacing:.05em; text-transform:uppercase;
  color:var(--accent); padding:.2rem 0; user-select:none;
}
.charm-dano-colar > summary::-webkit-details-marker{ display:none; }
.charm-dano-colar > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.charm-dano-colar[open] > summary::before{ transform:rotate(90deg); }
.charm-dano-colar > summary:hover{ color:var(--accent-strong); }
.charm-dano-colar p{ margin:.35rem 0 .4rem; line-height:1.5; }
.charm-dano-colar textarea{
  width:100%; resize:vertical; padding:.5rem .6rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text);
  font-family:var(--font-mono); font-size:.72rem; line-height:1.5;
}
.charm-dano-eco{ margin:.4rem 0 0 !important; font-size:.74rem; color:var(--online); line-height:1.45; }
.charm-dano-eco.is-erro{ color:var(--offline); }
.charm-dano-eco[hidden]{ display:none; }

/* ── Minor Charms: bolso separado, seção separada ── */
.charm-minor{ margin-top:.8rem; }
.charm-minor[hidden]{ display:none; }
.charm-minor > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.35rem;
  font-family:var(--font-mono); font-size:.66rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--voc-druid); padding:.2rem 0; user-select:none;
}
.charm-minor > summary::-webkit-details-marker{ display:none; }
.charm-minor > summary::before{ content:"▸"; font-size:.72rem; transition:transform .15s ease; display:inline-block; }
.charm-minor[open] > summary::before{ transform:rotate(90deg); }
.charm-minor > summary:hover{ color:var(--accent-strong); }
.charm-minor-intro{ margin:.4rem 0 .7rem !important; line-height:1.5; }
.charm-minor-bloco{
  margin-bottom:.8rem; padding:.6rem .7rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:color-mix(in oklch, var(--surface-2) 55%, transparent);
}
.charm-minor-bloco:last-child{ margin-bottom:0; }
.charm-minor-bloco h4{
  margin:0 0 .45rem; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.07em;
  text-transform:uppercase; color:var(--muted); font-weight:600;
}
.charm-minor-bloco p{ margin:.45rem 0 0; line-height:1.5; }
.charm-minor-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.3rem; }
.charm-minor-linha{ display:flex; align-items:center; gap:.4rem; font-size:.76rem; flex-wrap:wrap; }
.charm-minor-linha img{ width:18px; height:18px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; }
.charm-minor-nome{ font-weight:600; }
.charm-minor-seta{ color:var(--accent); font-weight:700; }
.charm-minor-bicho{ color:var(--accent-strong); font-weight:600; }
.charm-minor-valor{ margin-left:auto; font-family:var(--font-mono); font-variant-numeric:tabular-nums; color:var(--online); font-weight:700; }
.charm-minor-valor .muted{ font-weight:500; font-size:.66rem; }
.charm-minor-fora{ list-style:none; margin:.4rem 0 0; padding:0; display:flex; flex-direction:column; gap:.2rem; }
.charm-minor-fora li{ font-size:.72rem; color:var(--muted); padding-left:.8rem; position:relative; line-height:1.45; }
.charm-minor-fora li::before{ content:"—"; position:absolute; left:0; }
.charm-minor-situacionais{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.5rem; }
.charm-minor-situacionais li{ display:flex; gap:.45rem; align-items:flex-start; }
.charm-minor-situacionais img{ width:18px; height:18px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; margin-top:.15rem; opacity:.8; }
.charm-minor-situacionais p{ margin:0 !important; line-height:1.45; }
.charm-minor-quando{ font-style:italic; opacity:.85; }
@media (max-width:720px){
  .charm-minor-valor{ margin-left:0; flex-basis:100%; }
}

/* Os 11 Minor na coluna da esquerda, com o MESMO controle dos Major ("tenho" + tier).
   A borda muda de cor porque o bolso é outro: echoes, não charm points. */
.charm-minor-aviso{ margin:0 0 .7rem !important; line-height:1.5; }
.charm-item.is-minor{ border-left:3px solid color-mix(in oklch, var(--voc-druid) 55%, var(--border)); }
.charm-item.is-minor .charm-name{ color:var(--voc-druid); }
.charm-minor-desligados{ margin:.7rem 0 0 !important; line-height:1.5; }
/* O tier escolhido, dito em número: sem isto o texto lista "6% / 9% / 12%" e não
   diz qual dos três é o seu. */
.charm-minor-notier{ font-size:.68rem; color:var(--muted); font-weight:400; margin-left:.35rem; }
.charm-minor-notier strong{ color:var(--voc-druid); font-weight:700; }

/* Medir golpes por bicho, dentro do painel do personagem. */
.charm-calibrar{ margin-top:.7rem; }
.charm-calibrar > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.63rem; letter-spacing:.05em; text-transform:uppercase;
  color:var(--accent); padding:.2rem 0; user-select:none;
}
.charm-calibrar > summary::-webkit-details-marker{ display:none; }
.charm-calibrar > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.charm-calibrar[open] > summary::before{ transform:rotate(90deg); }
.charm-calibrar > summary:hover{ color:var(--accent-strong); }
.charm-calibrar p{ margin:.35rem 0 .5rem; line-height:1.5; }
.charm-calibrar-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(9rem, 1fr)); gap:.6rem; }
.charm-calibrar-grid label{
  display:block; font-family:var(--font-mono); font-size:.6rem; letter-spacing:.05em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.2rem;
}
.charm-calibrar-grid input{
  width:100%; padding:.4rem .5rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-family:var(--font-mono); font-size:.85rem;
}
.charm-calibrar-grid > div > .muted{ display:block; margin-top:.15rem; line-height:1.3; }
.charm-cal-eco{ margin:.45rem 0 0 !important; font-size:.75rem; color:var(--online); line-height:1.45; }
.charm-cal-eco.is-erro{ color:var(--offline); }
.charm-cal-eco[hidden]{ display:none; }

/* Importar o Charm Data e conferir o modelo contra o medido. */
.charm-importar{ margin-bottom:.7rem; }
.charm-importar > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.63rem; letter-spacing:.05em; text-transform:uppercase;
  color:var(--accent); padding:.2rem 0; user-select:none;
}
.charm-importar > summary::-webkit-details-marker{ display:none; }
.charm-importar > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.charm-importar[open] > summary::before{ transform:rotate(90deg); }
.charm-importar > summary:hover{ color:var(--accent-strong); }
.charm-importar p{ margin:.35rem 0 .45rem; line-height:1.5; }
.charm-importar textarea{
  width:100%; resize:vertical; padding:.5rem .6rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text);
  font-family:var(--font-mono); font-size:.72rem; line-height:1.5;
}
.charm-importar-eco{ margin:.4rem 0 0 !important; font-size:.74rem; color:var(--online); line-height:1.45; }
.charm-importar-eco.is-erro{ color:var(--offline); }
.charm-importar-eco[hidden]{ display:none; }

.charm-conferencia{
  margin-top:.6rem; padding:.6rem .65rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:color-mix(in oklch, var(--surface-2) 60%, transparent);
}
.charm-conferencia[hidden]{ display:none; }
.charm-conferencia h4{
  margin:0 0 .4rem; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted); font-weight:600;
}
.charm-conf-tabela{ width:100%; min-width:0; font-size:.74rem; border-collapse:collapse; background:none; border:none; }
.charm-conf-tabela th{
  font-family:var(--font-mono); font-size:.58rem; text-transform:uppercase; letter-spacing:.05em;
  color:var(--muted); font-weight:600; text-align:left; padding:.15rem .3rem; background:none; border:none;
}
.charm-conf-tabela td{ padding:.18rem .3rem; border:none; border-top:1px solid var(--border); }
.charm-conf-tabela .num{ text-align:right; font-family:var(--font-mono); font-variant-numeric:tabular-nums; }
.charm-conf-bate{ color:var(--online); }
.charm-conf-perto{ color:var(--warn); }
.charm-conf-longe{ color:var(--offline); }

@media (max-width: 720px){
  /* A tabela de conferência tem 4 colunas e a última ("onde estava") era cortada.
     No celular cada charm vira um bloco: os números na primeira linha, o bicho
     embaixo, com os rótulos vindo do data-rotulo já que o thead sai de cena. */
  .charm-conf-tabela thead{ display:none; }
  .charm-conf-tabela, .charm-conf-tabela tbody, .charm-conf-tabela tr, .charm-conf-tabela td{ display:block; }
  .charm-conf-tabela tr{
    display:grid; grid-template-columns:1fr auto auto; gap:.15rem .5rem;
    padding:.4rem 0; border-top:1px solid var(--border); align-items:baseline;
  }
  .charm-conf-tabela td{ border:none; padding:0; }
  .charm-conf-tabela td::before{
    content:attr(data-rotulo) " ";
    font-family:var(--font-mono); font-size:.54rem; text-transform:uppercase;
    letter-spacing:.05em; color:var(--muted); margin-right:.25rem;
  }
  .charm-conf-tabela td:first-child::before{ display:none; }
  .charm-conf-onde{ grid-column:1 / -1; }
}

/* ═══ Exaltation Forge ═══ */
.fg-layout{
  display:grid; grid-template-columns:minmax(280px, 24rem) minmax(0, 1fr);
  gap:1rem; padding:1.2rem 1.8rem 3rem; align-items:start;
  max-width:1500px; margin-inline:auto;
}
.fg-controles{ padding:1.25rem 1.35rem; }
.fg-campo{ margin-bottom:.8rem; }
.fg-campo label{
  display:block; font-family:var(--font-mono); font-size:.66rem; letter-spacing:.07em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.25rem;
}
.fg-campo select{
  width:100%; padding:.45rem .55rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-family:var(--font-body); font-size:.82rem; cursor:pointer;
}
.fg-campo input[type=range]{ width:100%; accent-color:var(--accent); }
.fg-tier-rotulo{
  font-family:var(--font-mono); font-size:.78rem; color:var(--accent-strong);
  background:var(--accent-soft); border-radius:var(--r-sm); padding:.05rem .35rem; margin-left:.3rem;
}
.fg-bonus-bloco{
  margin:.9rem 0; padding:.6rem .7rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface-2); display:flex; flex-direction:column; gap:.35rem;
}
.fg-bonus{ font-size:.8rem; color:var(--text); align-items:flex-start; }
.fg-bonus input{ margin-top:.15rem; }
/* No modo ideal nada falha, então os bônus não mudam nada — a tela mostra isso. */
.fg-bonus.is-off{ opacity:.45; }
.fg-aviso{
  margin:.3rem 0 0; padding:.4rem .55rem; font-size:.74rem; line-height:1.45;
  border:1px solid color-mix(in oklch, var(--warn) 45%, transparent); border-left-width:3px;
  border-radius:var(--r-md); background:var(--warn-soft); color:var(--text);
}
.fg-aviso[hidden]{ display:none; }
.fg-sub{ margin-top:1.1rem; }
.fg-precos{ display:grid; grid-template-columns:1fr 1fr; gap:.7rem; }
.fg-precos label{
  display:block; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.2rem;
}
.fg-precos input{
  width:100%; padding:.42rem .5rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-family:var(--font-mono); font-size:.82rem;
}

.fg-saida{ min-width:0; }
.fg-panel{ padding:1.25rem 1.35rem; }
.fg-numeros{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(9rem, 1fr)); gap:.8rem; margin-bottom:1rem;
}
.fg-num{ display:flex; flex-direction:column; gap:.1rem; }
.fg-num strong{ font-family:var(--font-mono); font-size:1.3rem; color:var(--accent-strong); line-height:1.1; }
.fg-total{ margin-top:.9rem; padding-top:.8rem; border-top:1px solid var(--border); }
.fg-total .charm-total-linha strong{ font-size:1.25rem; }
.fg-total p{ margin:.35rem 0 0; line-height:1.5; }
.fg-dica{ margin-top:.8rem !important; padding:.5rem .6rem; border:1px dashed var(--border); border-radius:var(--r-md); }
.fg-passos{ margin-top:.9rem; }
.fg-passos > summary{
  cursor:pointer; list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  font-family:var(--font-mono); font-size:.64rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--muted); padding:.2rem 0; user-select:none;
}
.fg-passos > summary::-webkit-details-marker{ display:none; }
.fg-passos > summary::before{ content:"▸"; font-size:.7rem; transition:transform .15s ease; display:inline-block; }
.fg-passos[open] > summary::before{ transform:rotate(90deg); }
.fg-passos > summary:hover{ color:var(--accent-strong); }
.fg-tabela{ padding:0; overflow-x:auto; margin-top:.4rem; }
.fg-tabela table{ min-width:22rem; font-size:.8rem; }
@media (max-width: 720px){
  .fg-layout{ grid-template-columns:1fr; padding-left:1rem; padding-right:1rem; }
  .fg-precos{ grid-template-columns:1fr; }
}

/* O bloco dos Minor precisa parecer uma SEÇÃO, não um link de rodapé: ele é uma
   segunda distribuição inteira, e fechado num summary de 22px ninguém achava. */
.charm-minor{
  margin-top:1rem; padding:.7rem .8rem;
  border:1px solid color-mix(in oklch, var(--voc-druid) 35%, var(--border));
  border-left-width:3px; border-radius:var(--r-md);
  background:color-mix(in oklch, var(--surface-2) 45%, transparent);
}
.charm-minor > summary{
  font-size:.8rem; text-transform:none; letter-spacing:0; gap:.45rem; flex-wrap:wrap;
  color:var(--text);
}
.charm-minor-titulo{
  font-family:var(--font-display); font-weight:600; font-size:.92rem;
  letter-spacing:.02em; color:var(--voc-druid);
}
.charm-minor-sub{ font-family:var(--font-body); }
.charm-minor > summary::before{ color:var(--voc-druid); }

/* ═══ Hunt Analyzer ═══ */
.ha-layout{
  display:grid; grid-template-columns:minmax(300px, 30rem) minmax(0, 1fr);
  gap:1rem; padding:1.2rem 1.8rem 3rem; align-items:start;
  max-width:1500px; margin-inline:auto;
}
.ha-entrada{ padding:1.25rem 1.35rem; }
.ha-campo{ display:block; margin-bottom:.7rem; }
.ha-campo > span{
  display:block; font-family:var(--font-mono); font-size:.64rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.25rem;
}
.ha-campo textarea{
  width:100%; resize:vertical; padding:.55rem .65rem;
  border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text);
  font-family:var(--font-mono); font-size:.72rem; line-height:1.5;
}

.ha-saida{ display:flex; flex-direction:column; gap:1rem; min-width:0; }
.ha-saida[hidden]{ display:none; }
.ha-panel{ padding:1.25rem 1.35rem; }
.ha-numeros{ display:grid; grid-template-columns:repeat(auto-fit, minmax(8.5rem, 1fr)); gap:.8rem; }
.ha-num{ display:flex; flex-direction:column; gap:.1rem; }
.ha-num strong{ font-family:var(--font-mono); font-size:1.25rem; color:var(--accent-strong); line-height:1.15; }
.ha-num.ha-bom strong{ color:var(--online); }
.ha-num.ha-ruim strong{ color:var(--offline); }
.ha-nota{ margin:.8rem 0 0 !important; padding-top:.7rem; border-top:1px solid var(--border); line-height:1.5; }

.ha-barras{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.35rem; }
.ha-barras li{ display:flex; align-items:center; gap:.5rem; font-size:.8rem; }
.ha-barra-nome{ flex:0 0 9rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:600; }
.ha-barra{ flex:1 1 4rem; min-width:2rem; max-width:20rem; height:6px; background:var(--surface-3); border-radius:var(--r-pill); overflow:hidden; }
.ha-barra i{ display:block; height:100%; background:var(--bar-grad); border-radius:var(--r-pill); }
.ha-barra-valor{ flex:0 0 auto; min-width:5rem; text-align:right; font-family:var(--font-mono); font-variant-numeric:tabular-nums; }

.ha-bichos{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.3rem; }
.ha-bichos li{ display:flex; align-items:center; gap:.5rem; font-size:.82rem; padding:.25rem 0; border-bottom:1px solid var(--border); }
.ha-bichos li:last-child{ border-bottom:none; }
.ha-bichos img, .ha-sem-sprite{ width:28px; height:28px; object-fit:contain; flex:0 0 auto; }
.ha-bicho-nome{ flex:1 1 8rem; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:600; }
.ha-bicho-qtd{
  flex:0 0 auto; font-family:var(--font-mono); font-weight:700; color:var(--accent-strong);
  background:var(--accent-soft); border-radius:var(--r-sm); padding:.05rem .4rem; font-size:.76rem;
}
.ha-acoes{ display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; margin-top:.9rem; padding-top:.8rem; border-top:1px solid var(--border); }
.ha-acoes .btn-ghost{ text-decoration:none; display:inline-flex; align-items:center; }

@media (max-width: 720px){
  .ha-layout{ grid-template-columns:1fr; padding-left:1rem; padding-right:1rem; }
  .ha-barra-nome{ flex-basis:6rem; }
  .ha-barra-valor{ min-width:4rem; }
  .ha-entrada button, .ha-acoes .btn-ghost{ min-height:40px; }
}

/* ═══ Boss Tracker ═══
   Coluna de filtros fixa + grade de cartões. Cartão e não tabela porque cada boss
   tem de 1 a 9 lugares: numa tabela isso vira célula gigante ou linha repetida. */
.bt-layout{
  display:grid; grid-template-columns:minmax(260px, 22rem) minmax(0, 1fr);
  gap:1rem; padding:1.2rem 1.8rem 3rem; align-items:start;
  max-width:1500px; margin-inline:auto;
}
/* O `top` precisa descontar a topbar. MEDIDO em 12/08, com prova viva (o boss-tracker
   está em manutenção e não renderiza): com `top:.8rem` o painel grudava a 13 px enquanto
   a barra ocupa até 79 px — 66 px dele ficavam ESCONDIDOS atrás dela. Só apareceu agora
   porque até o ciclo passado o body tinha altura travada e sticky nenhum funcionava. */
.bt-controles{ padding:1.15rem 1.25rem; position:sticky; top:calc(var(--h-topbar) + .8rem); }
.bt-busca input{
  width:100%; padding:.5rem .65rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-size:.85rem;
}
.bt-campo{ margin-top:.7rem; }
.bt-campo label{
  display:block; font-family:var(--font-mono); font-size:.62rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted); margin-bottom:.2rem;
}
.bt-campo select{
  width:100%; padding:.42rem .55rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-size:.82rem;
}
.bt-filtro-marcados{ margin-top:.7rem; font-size:.75rem; }
.bt-ronda{
  margin-top:.9rem; padding:.6rem .7rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:color-mix(in oklch, var(--surface-2) 55%, transparent);
}
.bt-ronda p{ margin:0; line-height:1.5; }
.bt-ronda-linha{ font-size:.78rem; margin-bottom:.5rem !important; }
.bt-fonte{ margin:.8rem 0 0 !important; line-height:1.5; }

.bt-lista{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(19rem, 1fr));
  gap:.8rem; min-width:0; align-content:start;
}
.bt-card{ padding:.75rem .85rem; display:flex; flex-direction:column; gap:.55rem; }
/* Boss que a pessoa marcou ganha a faixa lateral — na grade de 98, é o que deixa a
   ronda dela visível sem precisar filtrar. */
.bt-card.is-na-ronda{ border-left:3px solid var(--accent); }
.bt-card-topo{ display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; }
.bt-sprite{ width:64px; height:64px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; }
.bt-card-info{ min-width:0; flex:1 1 8rem; display:flex; flex-direction:column; gap:.1rem; }
.bt-card-info h3{
  margin:0; font-family:var(--font-display); font-size:.95rem; font-weight:600;
  letter-spacing:.01em; overflow-wrap:anywhere;
}
.bt-marcar{ flex:0 0 auto; white-space:nowrap; }
.bt-card.is-na-ronda .bt-marcar{ color:var(--accent-strong); border-color:color-mix(in oklch, var(--accent) 45%, transparent); }
.bt-locais{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.3rem; }
.bt-locais li{ font-size:.76rem; display:flex; gap:.4rem; align-items:baseline; flex-wrap:wrap; line-height:1.4; }
/* O nome identifica o lugar; a coordenada é o que se clica. Um boss pode ter três
   pontos no mesmo lugar, e repetir o nome três vezes só faz o cartão crescer. */
.bt-local-nome{ flex:1 1 9rem; min-width:0; overflow-wrap:anywhere; }
.bt-coords{ display:flex; gap:.25rem; flex-wrap:wrap; }
/* Cada coordenada é uma pastilha, e não texto solto: três pontos no mesmo lugar
   viram "33208, 31241, 7 33237, 31240, 7" e ninguém acha onde um termina. */
.bt-coord{
  font-family:var(--font-mono); font-size:.66rem; color:var(--muted);
  font-variant-numeric:tabular-nums; white-space:nowrap;
  padding:.1rem .3rem; border:1px solid var(--border); border-radius:var(--r-sm);
  background:color-mix(in oklch, var(--surface-2) 60%, transparent);
}
a.bt-coord{ color:var(--accent-strong); text-decoration:none; }
a.bt-coord:hover{ border-color:var(--accent); color:var(--accent); }
.bt-video{ font-size:.74rem; color:var(--muted); text-decoration:none; }
.bt-video:hover{ color:var(--accent-strong); }

@media (max-width:900px){
  .bt-layout{ grid-template-columns:1fr; padding-left:1rem; padding-right:1rem; }
  .bt-controles{ position:static; }
  .bt-lista{ grid-template-columns:1fr; }
  .bt-marcar{ min-height:40px; }
}

/* ═══ Planejador de Bestiário ═══ */
.bs-resumo{ padding:1.2rem 1.8rem 0; max-width:1500px; margin-inline:auto; }
.bs-resumo-painel{ padding:1rem 1.2rem; }
.bs-numeros{ display:flex; gap:1.6rem; flex-wrap:wrap; }
.bs-num{ display:flex; flex-direction:column; gap:.1rem; min-width:8rem; }
.bs-num strong{
  font-family:var(--font-display); font-size:1.5rem; font-weight:700; line-height:1.1;
  color:var(--accent-strong); font-variant-numeric:tabular-nums;
}
.bs-metas{ margin:.9rem 0 0; display:flex; gap:.5rem; flex-wrap:wrap; align-items:baseline; }
.bs-meta{
  font-size:.74rem; padding:.15rem .45rem; border:1px solid var(--border);
  border-radius:var(--r-sm); background:color-mix(in oklch, var(--surface-2) 60%, transparent);
}
.bs-metas-vazio{ margin:.8rem 0 0 !important; line-height:1.5; }

.bs-controles{
  display:flex; gap:.5rem; flex-wrap:wrap; align-items:center;
  padding:1rem 1.8rem .3rem; max-width:1500px; margin-inline:auto;
}
.bs-controles input[type="search"], .bs-controles select{
  padding:.42rem .6rem; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-size:.82rem;
}
.bs-controles input[type="search"]{ min-width:12rem; }
.bs-progresso{ padding:.3rem 1.8rem 0; max-width:1500px; margin-inline:auto; }
.bs-tabela{ max-width:1500px; margin-inline:auto; }

.bs-bicho{ display:flex; align-items:center; gap:.5rem; min-width:0; }
.bs-bicho img{ width:32px; height:32px; object-fit:contain; image-rendering:pixelated; flex:0 0 auto; }
.bs-bicho > span{ display:flex; flex-direction:column; min-width:0; }
.bs-bicho .small{ font-size:.66rem; }

/* A dificuldade é a régua de esforço da entrada; cor ajuda a varrer 865 linhas. */
.bs-dif{
  font-family:var(--font-mono); font-size:.64rem; letter-spacing:.04em; text-transform:uppercase;
  padding:.1rem .35rem; border-radius:var(--r-sm); border:1px solid currentColor; white-space:nowrap;
}
.dif-inofensivo{ color:var(--muted); }
.dif-trivial{ color:var(--online); }
.dif-facil{ color:var(--voc-druid); }
.dif-medio{ color:var(--accent-strong); }
.dif-dificil{ color:var(--warn); }
.dif-desafiador{ color:var(--offline); }

.bs-prog{ display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; font-size:.72rem; }
.bs-prog-barra{
  flex:1 1 4rem; min-width:3rem; height:6px; border-radius:999px;
  background:color-mix(in oklch, var(--border) 70%, transparent); overflow:hidden;
}
.bs-prog-barra i{ display:block; height:100%; background:linear-gradient(90deg, var(--accent), var(--accent-strong)); }
.bs-prog-txt{ white-space:nowrap; }
tr.is-pronta .bs-prog-txt{ color:var(--online); font-weight:600; }
tr.is-pronta td{ background:color-mix(in oklch, var(--online) 6%, transparent); }

@media (max-width:900px){
  .bs-resumo, .bs-controles, .bs-progresso{ padding-left:1rem; padding-right:1rem; }
  .bs-numeros{ gap:1rem; }
  .bs-num strong{ font-size:1.2rem; }
  .bs-controles input[type="search"], .bs-controles select{ flex:1 1 100%; }
}

@media (max-width:720px){
  /* Sete colunas não cabem em 375px, e esta tabela não é um ranking para varrer com
     o olho: é uma decisão por linha ("vale a pena caçar isto?"). Então no celular
     cada criatura vira um cartão, com os rótulos vindo do data-rotulo — o mesmo
     recurso que a tabela de conferência dos charms já usa. */
  #bs-table thead{ display:none; }
  #bs-table, #bs-table tbody, #bs-table tr, #bs-table td{ display:block; }
  /* `table{min-width:800px}` é global e serve às tabelas que rolam na horizontal.
     Empilhado, ele empurrava a segunda coluna do grid para fora da tela: charm
     points e rendimento existiam, mediam 391px e começavam em x=421 num aparelho
     de 375px. Ninguém veria as duas colunas que são o motivo da página existir. */
  #bs-table{ min-width:0; }
  #bs-table tr{
    display:grid; grid-template-columns:1fr 1fr; gap:.2rem .6rem;
    padding:.6rem .2rem; border-top:1px solid var(--border);
  }
  #bs-table td{ border:none; padding:0; font-size:.74rem; }
  #bs-table td:first-child, #bs-table td:last-child{ grid-column:1 / -1; }
  #bs-table td.num{ text-align:left; }
  #bs-table td:not(:first-child)::before{
    content:attr(data-rotulo) " ";
    font-family:var(--font-mono); font-size:.54rem; text-transform:uppercase;
    letter-spacing:.05em; color:var(--muted); margin-right:.25rem;
  }
  #bs-table td[data-rotulo="Meu progresso"]::before{ display:block; margin-bottom:.15rem; }
}

/* ═══ placar de rivalidades entre guildas (feed de mortes lido como guerra) ═══
   Veio da comparação com o rubinottools.com (12/08): eles têm "War & Statistics" com as
   principais rivalidades do mês. O dado já era nosso — faltava cruzar `is_player` com a
   guilda de cada nome.
   A barra é o elemento central: ela diz em UM olhar quem está ganhando, coisa que dois
   números lado a lado não dizem. Segue a anatomia de sulco+cilindro das outras barras
   do site (trilho escavado, preenchimento com relevo). */
.riv-painel{ margin:0 1.8rem 1.2rem; padding:1.1rem 1.25rem; }
.riv-cabeca{ display:flex; align-items:center; justify-content:space-between; gap:.8rem; flex-wrap:wrap; }
.riv-cabeca h2{ margin:0; font-size:1.02rem; }
.riv-periodo{ display:flex; align-items:center; gap:.4rem; }
.riv-sub{ margin:.35rem 0 .8rem; }
.riv-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.55rem; }
.riv-carregando{ padding:.6rem 0; }
.riv-item{
  display:flex; align-items:flex-start; gap:.7rem;
  padding:.6rem .7rem; border-radius:var(--r-md);
  background:var(--surface-2); border:1px solid var(--border);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 1px 2px oklch(38% .06 250 / .18);
}
html[data-theme="dark"] .riv-item{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .06),
    0 1px 3px oklch(0% 0 0 / .38);
}
/* A posição é uma peça cunhada, como a medalha do pódio — mesma linguagem. */
.riv-pos{
  flex:0 0 1.6rem; height:1.6rem; display:grid; place-items:center;
  border-radius:var(--r-sm); background:var(--surface-3); color:var(--muted);
  font-family:var(--font-mono); font-size:.72rem; font-weight:700;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .45),
    inset 0 -1px 0 oklch(0% 0 0 / .14);
}
html[data-theme="dark"] .riv-pos{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    inset 0 -1px 0 oklch(0% 0 0 / .3);
}
.riv-corpo{ flex:1 1 auto; min-width:0; }
.riv-nomes{ display:flex; align-items:baseline; gap:.5rem; flex-wrap:wrap; }
.riv-guilda{ color:var(--text); text-decoration:none; font-weight:600; font-size:.86rem; }
.riv-guilda:hover{ color:var(--accent-strong); }
/* Quem está na frente recebe o acento: o vencedor se lê antes do placar. */
.riv-guilda-frente{ color:var(--accent-strong); }
.riv-placar{ font-family:var(--font-mono); font-size:.8rem; font-weight:700; color:var(--text); white-space:nowrap; }
.riv-x{ color:var(--muted); font-weight:400; margin:0 .1rem; }
/* Trilho escavado + duas faixas: a divisão entre elas É o placar. */
.riv-barra{
  display:flex; height:.4rem; margin:.4rem 0 .35rem; border-radius:var(--r-pill);
  overflow:hidden; background:var(--surface-3);
  box-shadow:
    inset 0 1px 2px oklch(0% 0 0 / .18),
    inset 0 -1px 0 oklch(100% 0 0 / .12);
}
.riv-barra i{ display:block; height:100%; }
.riv-barra-a{ background:var(--accent); box-shadow:inset 0 1px 0 oklch(100% 0 0 / .3); }
.riv-barra-b{ background:var(--offline); box-shadow:inset 0 1px 0 oklch(100% 0 0 / .3); }
.riv-meta{ display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }

/* ── volume nas cartas de confronto ──
   A família `riv-*` nasceu no ciclo 44, DEPOIS do sistema de profundidade, e era a única
   do site ainda inteiramente plana: medido na tela, todos os `riv-item` com
   `perspective: none` e `transform-style: flat`.

   A perspectiva mora na LISTA, não na carta — mesma razão da grade da home: doze
   confrontos com doze câmeras independentes leem como doze caixas, e com uma câmera só
   leem como uma pilha de cartas sobre a mesa. Reaproveita `--fuga-x/--fuga-y`, que o
   shared.js já escreve.

   1200px, MAIS que os 1000px da home, e isto é o contrário do que parece: comecei em
   900px achando que carta larga pedia câmera curta, e a medição mostrou o inverso.
   Elevar algo em Z não só o amplia — também o empurra para longe do ponto de fuga, e
   quanto mais longe do centro ele estiver, mais forte é esse empurrão. Numa carta de
   1165 px a medalha mora na ponta esquerda, e a 900px ela ESCAPAVA 2,1 px para fora da
   borda (pior caso com a fuga em 75%, que o ponteiro alcança). Afastar a câmera achata
   esse deslocamento sem tirar o relevo: a 1200px sobra 4,5 px de folga dentro dos 11,2 px
   de padding. */
.tem-3d .riv-lista{
  perspective:240vw;
  perspective-origin:var(--fuga-x, 50%) var(--fuga-y, 40%);
  transition:perspective-origin .25s ease;
}
.tem-3d .riv-item{
  transform-style:preserve-3d;
  will-change:transform;
  transition:transform .25s cubic-bezier(.22,.61,.36,1), box-shadow .18s ease, border-color .18s ease;
}
.tem-3d .riv-item:hover{
  /* Sobe e inclina. O `--rx/--ry` vem do ponteiro e cai a 0 quando ele sai, então esta
     mesma regra serve para inclinar e para voltar ao plano. */
  border-color:color-mix(in oklch, var(--accent) 42%, var(--border));
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .5),
    0 2px 4px oklch(38% .06 250 / .16),
    0 12px 24px -14px oklch(38% .06 250 / .34);
}
.tem-giro .riv-item:hover{ transform:translateY(-2px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }
html[data-theme="dark"] .tem-3d .riv-item:hover{
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / .07),
    0 2px 5px oklch(0% 0 0 / .4),
    0 14px 28px -16px oklch(0% 0 0 / .6);
}

/* ── as camadas ──
   Permanentes, não só no hover: a carta já É volumétrica e o giro apenas revela isso. Se
   as camadas nascessem no hover, o conteúdo saltaria ao ser apontado.

   Os valores são deliberadamente desiguais por LARGURA, não por importância. Com
   `perspective`, `translateZ` também amplia: 22px de Z engordam o elemento em ~2%. Na
   medalha (26 px) isso é meio pixel; na barra (1104 px) seriam ~20 px de transbordo — e
   transbordo em elemento largo é exatamente como se cria rolagem horizontal, que este
   site não pode ter. Por isso o que é largo quase não sobe.

   A medalha desce de 22 para 16 pelo motivo explicado no bloco da lista: aos 22 ela
   escapava da borda da carta no pior ângulo de fuga. */
/* `.riv-corpo` é um invólucro sem aparência própria, mas PRECISA entrar no 3D: a
   perspectiva só alcança um descendente se TODA a cadeia até ele for `preserve-3d`. Um
   único `flat` no meio achata tudo que vem abaixo — medido na tela: a medalha, filha
   direta da carta, escalava 2,51%, enquanto nomes, barra e meta, que moram aqui dentro,
   ficavam em 0,00%. As três camadas existiam no CSS e não apareciam. */
.tem-3d .riv-corpo{ transform-style:preserve-3d; }
.tem-3d .riv-pos{ transform:translateZ(16px); }
.tem-3d .riv-nomes{ transform:translateZ(10px); }
.tem-3d .riv-meta{ transform:translateZ(6px); }
/* A barra fica no CHÃO da carta de propósito: é um sulco escavado na superfície, e sulco
   que levanta deixa de ser sulco. */
.tem-3d .riv-barra{ transform:translateZ(1px); }

/* Luz do ponteiro na face inclinada, como nos cartões da home. Fica atrás do conteúdo e
   não intercepta clique. O `--luz-branco` (85% no claro, 0% no escuro) existe porque no
   tema claro uma luz de acento pura ESCURECE o que devia iluminar. */
.tem-3d .riv-item{ position:relative; }
.tem-3d .riv-item::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:0;
  border-radius:inherit; transition:opacity .2s ease;
  background:radial-gradient(circle at var(--luz-x, 50%) var(--luz-y, 50%),
    color-mix(in oklch,
      color-mix(in oklch, oklch(100% 0 0) var(--luz-branco, 0%), var(--accent))
      var(--luz-alfa-card, 22%), transparent),
    transparent 60%);
}
.tem-3d .riv-item:hover::before{ opacity:1; }

/* Cinto de segurança: se o navegador respeitar a preferência mas o JS não tiver rodado
   (falha de script, cache velho), o CSS desliga o efeito por conta própria. */
@media (prefers-reduced-motion: reduce){
  /* Só o giro sai; as camadas em translateZ ficam (ver o bloco do .summary, linha ~476). */
  .tem-3d .riv-item, .tem-3d .riv-item:hover{ transform:none; transition:none; }
}
@media (max-width: 720px){
  .riv-painel{ margin:0 1rem 1rem; }
  .riv-nomes{ gap:.35rem; }
  .riv-guilda{ font-size:.8rem; }
}

/* ═══ ficha de personagem: PvP, bazaar e percentil ═══ */
/* Três blocos acrescentados em 31/08 à ficha individual. O dado já existia no banco e
   nunca chegava aqui: o feed de mortes lido pelo indivíduo, a passagem pelo bazaar e a
   posição entre os pares. */

/* As três colunas do PvP (vítimas, nêmesis, criaturas) são listas curtas e independentes:
   `auto-fit` deixa elas se acomodarem sozinhas — três no desktop, uma no celular — sem
   media query. O mínimo de 15rem é o ponto em que um nome de personagem ainda cabe numa
   linha só. */
.pvp-grid{
  display:grid; gap:1rem; margin-bottom:1.1rem;
  grid-template-columns:repeat(auto-fit, minmax(15rem, 1fr));
}
.pvp-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.1rem; }
.pvp-lista li{
  display:flex; align-items:center; justify-content:space-between; gap:.6rem;
  padding:.4rem .55rem; border-radius:var(--r-sm);
  border-bottom:1px solid color-mix(in oklch, var(--border) 55%, transparent);
}
.pvp-lista li:last-child{ border-bottom:0; }
.pvp-lista li:hover{ background:color-mix(in oklch, var(--surface-3) 55%, transparent); }
/* A contagem é o número que se compara entre as linhas: tabular para as casas alinharem
   verticalmente, como no resto do site. */
.pvp-n{
  font-family:var(--font-mono); font-size:.78rem; font-variant-numeric:tabular-nums;
  color:var(--accent-strong); font-weight:600; white-space:nowrap;
}

/* Tabela compacta para o histórico de bazaar — poucas linhas, dentro de um painel. */
.mini-tabela{ width:100%; border-collapse:collapse; font-size:.82rem; }
.mini-tabela th{
  text-align:left; font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase;
  letter-spacing:.1em; color:var(--muted); font-weight:500; padding:.4rem .5rem;
  border-bottom:1px solid var(--border-strong);
}
.mini-tabela td{ padding:.45rem .5rem; border-bottom:1px solid color-mix(in oklch, var(--border) 55%, transparent); }
.mini-tabela tr:last-child td{ border-bottom:0; }
.mini-tabela .num{ text-align:right; font-variant-numeric:tabular-nums; }

/* O cartão de percentil mora na mesma grade dos outros da aba Personagem, mas carrega a
   única informação COMPARATIVA da ficha — por isso ganha a cor de acento na borda. */
.percentil-card{ border-color:color-mix(in oklch, var(--accent) 42%, var(--border)); }
.percentil-card .value{ color:var(--accent-strong); }
.percentil-card .small{ margin-top:.25rem; line-height:1.4; }
