
@import url('https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@700;900&family=Cinzel:wght@400;600&family=Spectral:ital,wght@0,400;0,500;0,600;1,400&family=EB+Garamond:ital,wght@0,400;0,600;1,400&display=swap');

:root {
  --bg: #0a0810; --bg2: #14101c; --card: #1a1424; --gold: #c9a24a; --gold-bright: #e8c876;
  --raven: #8a76c9; --text: #e6e0f0; --dim: #9a8fb0; --border: #2e2540; --border-lit: #4a3a66;
  --brass-dark: #6b4f26;

  /* Escala tipográfica. Antes eram 18 tamanhos soltos, nove deles espremidos entre
     0,82rem e 1,1rem — uma faixa que o olho lê como "tudo do mesmo tamanho" enquanto
     destrói qualquer hierarquia. São sete degraus agora, cada um com salto visível
     (~1,15x embaixo, abrindo para ~1,45x no alto, que é como uma escala se comporta:
     texto corrido precisa de passos curtos, display precisa de passos largos).

     Quem escreve CSS novo aqui usa um destes. Um literal solto é deriva, e o
     detector do impeccable acusa. */
  --fs-micro: 0.8rem;      /* etiqueta em versal, tag, cabeçalho de tabela */
  --fs-small: 0.92rem;     /* meta, legenda, célula de tabela, rodapé */
  --fs-body: 1.05rem;      /* texto corrido */
  --fs-large: 1.25rem;     /* linha de abertura, h3, título de card, CTA */
  --fs-section: 1.55rem;   /* h2 */
  --fs-title: 2.2rem;      /* h1 de página */
  --fs-hero: clamp(2.2rem, 6vw, 3.2rem);  /* h1 do banner da home */

  /* Desaceleracao exponencial: comeca rapido e para devagar, que e como coisa
     com massa se move. Uma curva so no site inteiro, a mesma do /play. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Uma tela larga tem espaço para respirar e a distância de leitura é maior. A escala
   inteira sobe junto, num lugar só, para as proporções entre os degraus não mudarem
   com o breakpoint. Mesmo 821px do resto do layout. */
@media (min-width: 821px) {
  :root {
    --fs-micro: 0.82rem; --fs-small: 1rem; --fs-body: 1.18rem;
    --fs-large: 1.4rem; --fs-section: 1.75rem; --fs-title: 2.6rem;
  }
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0; color: var(--text); overflow-x: hidden;
  font-family: 'Spectral', 'EB Garamond', 'Georgia', serif; line-height: 1.7; font-size: var(--fs-body);
  background-color: var(--bg);
  /* World art as a fixed sitewide backdrop on every page — absolute "/assets/..." path so it
     resolves the same regardless of how deep the current page sits (no depth-prefixing needed). */
  background-image:
    radial-gradient(ellipse 80% 50% at 50% -10%, rgba(138,118,201,0.14), transparent),
    radial-gradient(ellipse 60% 40% at 100% 100%, rgba(201,162,74,0.06), transparent),
    linear-gradient(180deg, rgba(10,8,16,0.72), rgba(10,8,16,0.86) 40%, var(--bg) 92%),
    url('/assets/lore/eldervast_world.webp');
  background-size: auto, auto, auto, cover;
  background-position: center, center, center, center 30%;
  background-repeat: no-repeat;
  background-attachment: fixed;
}
/* Faint parchment/paper-grain texture over everything, dark-fantasy "painted canvas" feel */
body::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 0; opacity: 0.5;
  background-image: repeating-radial-gradient(circle at 20% 30%, rgba(255,255,255,0.012) 0, transparent 2px),
    repeating-radial-gradient(circle at 70% 80%, rgba(255,255,255,0.01) 0, transparent 3px);
  background-size: 5px 5px, 7px 7px;
}
h1, h2, h3, .brand, .plate .name, .card-title { font-family: 'Cinzel', 'Georgia', serif; }

/* Desktop reads body text at a slightly larger size — Spectral is heavier/more distinctive
   than the previous EB Garamond, but at the same 1.05rem it read cramped on wide screens
   with plenty of room to breathe. Mobile keeps the tighter size (screen space is scarcer
   there and the two-column layouts already needed it). Same 820px breakpoint as the rest
   of the site's mobile layout, just as a min-width instead of a max-width. */


/* Home's third line under the two CTAs — the browser client (/play). Deliberately
   quiet: it is useless to a visitor with no character, so it must not compete with
   the two buttons above it, which are the ones a first-time reader needs.

   Not dead code: `WEB_CLIENT_LIVE` (top of this file) is False until the domain is
   registered with BotFather, so the markup this styles is not emitted yet. */
.hero-aside { margin: 1.4rem 0 0; font-size: var(--fs-small); color: var(--dim); }
.hero-aside a { color: var(--gold); text-decoration-color: rgba(201,162,74,0.45); }
.hero-aside a:hover { color: var(--gold-bright); }

/* Medida de leitura. O conteudo tem ~900px entre os dois trilhos num monitor de
   1440, o que punha a prosa em 90-123 caracteres por linha -- o olho perde a linha
   seguinte bem antes disso. 68ch poe o texto na faixa de 65-75 sem mexer em nada
   que seja tabela, grade de cartas ou arte, que querem a largura toda. */
main > p, main > ul, main > ol, .subtitle, .feature p, .coming-soon p {
  max-width: 68ch;
}
.banner .subtitle, .banner p { max-width: 640px; margin-left: auto; margin-right: auto; }

/* ---------------------------------------------------------------------------
   Movimento. Um momento autoral, nao efeito espalhado.

   O mundo inteiro e iluminado por uma fonte de luz so, entao e ela que fica viva
   quando nada esta acontecendo: um crescer e minguar lento sobre o canto superior
   esquerdo, dezoito segundos por ciclo -- devagar demais para se notar olhando, e
   exatamente o que faz a superficie parecer iluminada em vez de impressa. Roda
   sobre um pseudo-elemento fixo, so em opacity e transform, entao nao custa
   layout nem repaint de conteudo.
--------------------------------------------------------------------------- */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(ellipse 60% 45% at 20% 2%, rgba(255,214,150,0.10), transparent 72%);
  animation: candle 18s ease-in-out infinite;
}
@keyframes candle {
  0%, 100% { opacity: 0.55; transform: translate3d(0,0,0) scale(1); }
  38%      { opacity: 1;    transform: translate3d(0.6%,-0.4%,0) scale(1.05); }
  64%      { opacity: 0.72; transform: translate3d(-0.4%,0.5%,0) scale(0.99); }
}

/* A entrada do conteudo. O JS marca o container e o observador vai soltando os
   filhos conforme entram na tela, com um atraso curto entre vizinhos -- e uma
   coisa aparecendo depois da outra, nao um bloco inteiro piscando.

   O conteudo e visivel por padrao: quem esconde e a classe `.reveal`, que so o
   JS poe. Sem JS, com JS quebrado ou para um leitor de tela, a pagina esta
   inteira ali. Esconder no CSS e revelar no JS e como uma pagina fica em branco
   para sempre por causa de um erro de script. */
.reveal-item {
  opacity: 0; transform: translateY(14px);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
  transition-delay: calc(var(--i, 0) * 55ms);
}
.reveal-item.is-in { opacity: 1; transform: none; }

/* Quem pediu menos movimento recebe a pagina parada -- inclusive a vela, que e
   justamente o tipo de pulsacao continua que incomoda quem tem sensibilidade
   vestibular. Este site nao respeitava essa preferencia em lugar nenhum. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important; scroll-behavior: auto !important;
  }
  .reveal-item { opacity: 1; transform: none; }
}

/* Global link default — every other rule in this file is a specific class (.card, .tag,
   nav a, ...) with its own higher-specificity styling; this is only the fallback for a
   plain inline <a> in body text (e.g. "see the Items page") so it never falls through to
   the browser's default blue/purple, which would clash badly with this palette. */
a { color: var(--raven); text-decoration-color: rgba(138,118,201,0.45); text-underline-offset: 2px; transition: color 0.15s, text-decoration-color 0.15s; }
a:hover { color: var(--gold-bright); text-decoration-color: var(--gold); }

/* As partes do navegador que ninguem desenha. O anel de foco padrao do Chrome e
   azul-claro e a selecao e azul-royal: as duas unicas coisas nesta pagina que nao
   pertencem a este mundo, e as duas que so aparecem para quem navega por teclado
   ou marca um texto. Mesmos valores do /play, para os dois lados combinarem. */
:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 3px; border-radius: 3px; }
::selection { background: rgba(201,162,74,0.32); color: var(--gold-bright); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(0,0,0,0.35); }
::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #40311c, #241a10);
  border: 1px solid var(--brass-dark); border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover { background: linear-gradient(180deg, #5b452a, #33260f); }
html { scrollbar-color: #40311c rgba(0,0,0,0.35); scrollbar-width: thin; }

/* Numeros que mudam ou que sao comparados linha a linha (dano, preco, nivel, taxa
   de drop) alinham por coluna. Uma tabela de taxas com digitos de larguras
   diferentes obriga a ler numero por numero em vez de varrer a coluna. */
table, .stat-row, .card-meta { font-variant-numeric: tabular-nums; }

/* Tibia-style layout: fixed vertical nav rail on the left instead of a top header bar. */
.layout { display: flex; align-items: flex-start; min-height: 100vh; }
.sidebar {
  width: 232px; flex-shrink: 0; position: sticky; top: 0; height: 100vh; overflow-y: auto;
  background: linear-gradient(180deg, #17111f, #0a0810 90%); border-right: 1px solid var(--border);
  padding: 1.5rem 1.1rem; display: flex; flex-direction: column; gap: 1.6rem; z-index: 10;
}
.sidebar-top { display: flex; align-items: center; justify-content: center; }
.sidebar .brand {
  font-family: 'Cinzel Decorative', 'Cinzel', serif; font-size: var(--fs-large); color: var(--gold-bright);
  letter-spacing: 0.1em; text-decoration: none; text-shadow: 0 0 18px rgba(201,162,74,0.35);
  display: flex; align-items: center; justify-content: center; padding: 0.4rem 0 0.8rem;
  border-bottom: 1px solid var(--border); width: 100%;
}
.sidebar .brand-logo { height: 88px; width: auto; display: block; filter: drop-shadow(0 0 14px rgba(201,162,74,0.3)); }

/* Mobile menu toggle — hidden on desktop (the sidebar nav is always visible there). Same
   bronze-plate texture as the nav buttons it opens/closes, so it reads as part of the same
   UI family instead of a bolted-on hamburger icon. */
.nav-toggle {
  display: none; align-items: center; justify-content: center; flex-shrink: 0;
  width: 46px; height: 46px; padding: 0; cursor: pointer; border-radius: 7px;
  border: 2px solid #6b4f26;
  background-image: linear-gradient(160deg, rgba(28,20,38,0.86), rgba(14,10,20,0.93)), url('/assets/lore/metal_texture.webp');
  background-size: cover, 260px 260px; background-position: center, 20% 30%; background-blend-mode: normal, luminosity;
  box-shadow: inset 0 1px 0 rgba(255,224,160,0.22), inset 0 -3px 5px rgba(0,0,0,0.6), 0 3px 7px rgba(0,0,0,0.45);
}
.nav-toggle-icon, .nav-toggle-icon::before, .nav-toggle-icon::after {
  display: block; width: 20px; height: 2px; background: var(--gold-bright); border-radius: 2px;
  transition: transform 0.2s, opacity 0.2s;
}
.nav-toggle-icon { position: relative; }
.nav-toggle-icon::before, .nav-toggle-icon::after { content: ""; position: absolute; left: 0; }
.nav-toggle-icon::before { top: -6px; }
.nav-toggle-icon::after { top: 6px; }
.sidebar.nav-open .nav-toggle-icon { background: transparent; }
.sidebar.nav-open .nav-toggle-icon::before { transform: translateY(6px) rotate(45deg); }
.sidebar.nav-open .nav-toggle-icon::after { transform: translateY(-6px) rotate(-45deg); }

/* Sidebar nav buttons — real cracked-metal texture (a generated bronze/gold plate photo,
   tiled) under a dark tint, a bronze frame with beveled inset highlight/shadow, and an
   engraved-text effect (dark shadow below, thin gold glint above) so the labels read like
   they're cut into the plate rather than just colored text. */
nav { display: flex; flex-direction: column; gap: 0.55rem; }
nav a {
  position: relative; display: flex; align-items: center; gap: 0.7rem; color: #d8cbe8; text-decoration: none;
  font-family: 'Cinzel', 'Georgia', serif; font-size: var(--fs-small); letter-spacing: 0.05em;
  text-transform: uppercase; font-weight: 600;
  text-shadow: 0 1px 1px rgba(0,0,0,0.9), 0 -1px 0 rgba(255,224,160,0.12);
  padding: 0.75rem 0.85rem; border-radius: 7px; overflow: hidden;
  border: 2px solid #6b4f26;
  background-image: linear-gradient(160deg, rgba(28,20,38,0.86), rgba(14,10,20,0.93)), url('/assets/lore/metal_texture.webp');
  background-size: cover, 260px 260px; background-position: center, 20% 30%; background-blend-mode: normal, luminosity;
  box-shadow:
    inset 0 1px 0 rgba(255,224,160,0.22), inset 0 -3px 5px rgba(0,0,0,0.6),
    inset 2px 0 0 rgba(255,215,140,0.06), 0 3px 7px rgba(0,0,0,0.45);
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
nav a .nav-icon { width: 1.2em; height: 1.2em; display: flex; align-items: center; justify-content: center; opacity: 0.92; flex-shrink: 0; }
nav a .nav-icon svg { width: 100%; height: 100%; }
nav a:hover {
  color: var(--gold-bright); border-color: var(--gold);
  background-image: linear-gradient(160deg, rgba(38,28,50,0.82), rgba(20,14,28,0.9)), url('/assets/lore/metal_texture.webp');
  transform: translateX(2px) translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255,224,160,0.3), inset 0 -3px 5px rgba(0,0,0,0.6),
    inset 2px 0 0 rgba(255,215,140,0.1), 0 5px 16px rgba(0,0,0,0.5), 0 0 16px rgba(201,162,74,0.25);
}
nav a:active {
  transform: translateX(1px) translateY(0);
  box-shadow: inset 0 2px 6px rgba(0,0,0,0.7), inset 0 -1px 0 rgba(255,224,160,0.1);
}
.content-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* Persistent live-ranking rail on the right, present on every page (populated client-side
   from bot/api/main.py — the wiki is a static build, it has no live player state at
   generation time). Sticky so it stays in view while the main content scrolls. */
.ranking-sidebar {
  width: 240px; flex-shrink: 0; position: sticky; top: 1.5rem; align-self: flex-start;
  margin: 1.5rem 1.5rem 1.5rem 0; padding: 1.1rem 1.2rem;
  background: linear-gradient(160deg, var(--card), #16101f); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.4);
}
.ranking-sidebar h3 {
  margin: 0 0 0.8rem; font-size: var(--fs-small); color: var(--gold-bright); display: flex; align-items: center; gap: 0.5rem;
}
.ranking-sidebar ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.ranking-sidebar li { display: flex; justify-content: space-between; gap: 0.5rem; font-size: var(--fs-small); color: var(--text); }
.ranking-sidebar .rank-num { color: var(--dim); width: 1.4em; flex-shrink: 0; }
.ranking-sidebar .rank-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ranking-sidebar .rank-level { color: var(--gold); font-weight: 600; flex-shrink: 0; }
/* Presence dot next to a nick — rendered ONLY for someone actually online right now. An
   earlier version also drew a transparent placeholder for offline players to keep the names
   aligned; that read as a marker sitting next to every nick, so offline players now get no
   node at all. */
/* Deliberately NOT named .online-dot: that class already exists further down for the
   online-count badge, and reusing it here made the badge's rule win the cascade — every nick
   got a green dot regardless of presence, which is exactly the opposite of what this is for. */
/* Sits AFTER the nick, not before it: a leading dot indents the online player's name away
   from every offline name in the column, which reads as broken alignment. Trailing, the
   names all start on the same edge and the dot just hangs off the end. */
.rank-online-dot {
  display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: 50%;
  margin-left: 0.4rem; flex-shrink: 0; vertical-align: middle;
  background: #4ade80; box-shadow: 0 0 6px rgba(74, 222, 128, 0.9);
}
.ranking-sidebar a.see-all { display: block; margin-top: 0.9rem; font-size: var(--fs-micro); color: var(--raven); text-decoration: none; }
.ranking-sidebar a.see-all:hover { text-decoration: underline; }
/* Was 1180px, then 1024px — both still hid the sidebar on ordinary (non-maximized,
   non-ultrawide) desktop windows, which read as "the ranking sidebar doesn't appear" even
   on desktop. .content-wrap has min-width: 0 so it can shrink arbitrarily without breaking
   the layout, so there's no real width floor below which three columns stop fitting — only
   hide the sidebar once the whole layout switches to the stacked mobile view at 820px. */
@media (max-width: 820px) { .ranking-sidebar { display: none; } }

/* Mobile: the small top bar (brand logo + toggle strip) is gone entirely — every page
   already shows the big logo via the hero banner (Home's own banner, or the shared
   page_header on every other page). The toggle button instead floats fixed over that
   banner, top-right, so it reads as "next to the big logo" rather than a separate bar
   above it. The nav list itself opens as a floating dropdown under the toggle, not an
   inline block pushing the banner down. Opens/closes via the .nav-open class the script
   at the bottom of layout() toggles on #site-sidebar. */
@media (max-width: 820px) {
  /* .layout's base align-items: flex-start (needed on desktop so the sticky ranking-sidebar
     doesn't stretch to the tallest column) has to be overridden here — otherwise .sidebar
     below just shrink-wraps its content instead of spanning the full width like the rest
     of a stacked mobile layout expects. */
  .layout { flex-direction: column; align-items: stretch; }
  .sidebar {
    width: auto; position: static; height: auto; flex-direction: column; align-items: stretch;
    border-right: none; border-bottom: none; padding: 0; gap: 0;
  }
  /* The brand logo + online badge are hidden here on mobile — they're redundant with the
     big hero banner every page already renders right below. Only the toggle button
     survives from this row. */
  .sidebar-top { justify-content: flex-end; }
  .sidebar .brand, .sidebar .sidebar-online { display: none; }
  .nav-toggle {
    display: flex; position: fixed; top: 0.9rem; right: 1.1rem; z-index: 30;
  }
  nav {
    display: none; flex-direction: column; width: calc(100% - 2.2rem); gap: 0.4rem;
    max-height: 70vh; overflow-y: auto; position: fixed; top: 4rem; right: 1.1rem; left: 1.1rem;
    z-index: 29; background: linear-gradient(180deg, #17111f, #0a0810 90%);
    border: 1px solid var(--border); border-radius: 10px; padding: 0.8rem;
    box-shadow: 0 12px 34px rgba(0,0,0,0.55);
  }
  .sidebar.nav-open nav { display: flex; }
  nav a { padding: 0.6rem 0.8rem; font-size: var(--fs-micro); gap: 0.55rem; }
  nav a .nav-icon { width: 1.05em; height: 1.05em; }
}
.cta-block {
  background: linear-gradient(160deg, var(--card), #221a30); border: 1px solid var(--gold);
  border-radius: 10px; padding: 2rem 2.2rem; margin: 2.5rem 0; text-align: center;
  box-shadow: 0 0 40px rgba(0,0,0,0.4), inset 0 0 60px rgba(201,162,74,0.04);
}
/* Play CTA — "cracked old gold" plate: the same bronze/gold texture as the sidebar nav
   buttons, but in a brighter warm-gold tone (this is the primary action, not a nav item),
   with a deep 3D bevel and a raven-feather glyph standing in for the old ▶ play triangle. */
.cta-block a, .cta-inline, .cta-browser {
  position: relative; display: inline-flex; align-items: center; gap: 0.55rem;
  margin-top: 1rem; color: #2e1f08; font-weight: 700; text-decoration: none;
  font-family: 'Cinzel Decorative', 'Cinzel', serif; letter-spacing: 0.03em;
  padding: 0.85rem 2.1rem; border-radius: 9px; border: 2px solid #8a6420;
  background-image:
    linear-gradient(160deg, rgba(255,226,150,0.95), rgba(201,162,74,0.97)), url('/assets/lore/metal_texture.webp');
  background-size: cover, 280px 280px; background-blend-mode: normal, soft-light;
  text-shadow: 0 1px 0 rgba(255,255,255,0.4), 0 -1px 1px rgba(90,60,10,0.45);
  box-shadow:
    inset 0 2px 0 rgba(255,240,190,0.65), inset 0 -4px 6px rgba(90,60,10,0.55),
    inset 3px 0 0 rgba(255,240,190,0.18), 0 6px 18px rgba(0,0,0,0.5), 0 0 26px rgba(201,162,74,0.4);
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.cta-block a { font-size: var(--fs-large); }
.cta-inline { font-size: var(--fs-large); }
/* The browser button sits ABOVE the Telegram one and is the louder of the two: the immersive
   client is the thing being pushed, and two identical gold plates stacked would read as one
   button accidentally drawn twice. Brighter face, wider plate, and a screen glyph instead of
   the feather — same family, clearly not the same action. */
.cta-browser {
  font-size: var(--fs-large); margin-bottom: 0.15rem;
  background-image:
    linear-gradient(160deg, rgba(255,238,182,0.98), rgba(216,178,86,0.99)), url('/assets/lore/metal_texture.webp');
  border-color: #a67c28;
}
.cta-feather { width: 2.1em; height: 2.1em; flex-shrink: 0; }
.cta-block a:hover, .cta-inline:hover, .cta-browser:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 2px 0 rgba(255,240,190,0.75), inset 0 -4px 6px rgba(90,60,10,0.55),
    inset 3px 0 0 rgba(255,240,190,0.24), 0 10px 28px rgba(0,0,0,0.55), 0 0 34px rgba(201,162,74,0.55);
}
.cta-block a:active, .cta-inline:active, .cta-browser:active { transform: translateY(0); box-shadow: inset 0 3px 8px rgba(90,60,10,0.6); }
/* Narrow screens: .cta-block's own frame padding + the button's full padding stacked on top
   of main's own padding left too little room, so the "Play" button below the map wrapped
   onto two lines while the identical hero button above the map (no extra frame around it)
   stayed on one — same button class, but it visibly looked like two different buttons.
   Shrinking both paddings equally keeps the two buttons matching, which was the actual ask. */
@media (max-width: 480px) {
  .cta-block { padding: 1.4rem 0.6rem; }
  .cta-block a, .cta-inline, .cta-browser { padding: 0.8rem 1.3rem; font-size: var(--fs-body); }
  .cta-feather { width: 1.8em; height: 1.8em; }
}

/* Community group CTA — deliberately NOT the gold/bronze "Play" button styling, so it
   reads as a distinct action (join the community) rather than a second Play button. Uses
   Telegram's own blue plus a slow pulsing glow so it stays attention-grabbing without
   looking like an ad or being obnoxiously animated. */
.cta-community {
  display: inline-flex; align-items: center; gap: 0.6rem; margin-top: 1rem;
  padding: 0.85rem 2rem; border-radius: 999px; text-decoration: none;
  font-family: 'Cinzel Decorative', 'Cinzel', serif; font-size: var(--fs-body); font-weight: 700;
  letter-spacing: 0.03em; color: #fff; background: linear-gradient(135deg, #34b7f1, #1287c9);
  box-shadow: 0 6px 20px rgba(28,155,239,0.45), 0 0 0 rgba(52,183,241,0.5);
  animation: community-pulse 2.4s ease-in-out infinite;
  transition: transform 0.15s;
}
.cta-community:hover { transform: translateY(-2px); color: #fff; }
.cta-community svg { width: 1.4em; height: 1.4em; flex-shrink: 0; }
@keyframes community-pulse {
  0%, 100% { box-shadow: 0 6px 20px rgba(28,155,239,0.45), 0 0 0 0 rgba(52,183,241,0.45); }
  50% { box-shadow: 0 6px 24px rgba(28,155,239,0.6), 0 0 0 10px rgba(52,183,241,0); }
}
footer .cta-community { margin: 1.1rem 0.5rem 0; }

/* Tibia-style feature strip: bold headline + short paragraph, repeated down the homepage */
.feature-list { display: flex; flex-direction: column; gap: 1rem; margin: 3.5rem 0; }
.feature h2 { margin-top: 0.5rem; }
.feature p { color: #d8d0e6; font-size: var(--fs-body); }

/* Home: News/Roadmap + Beta rules section — a single swipeable carousel (one slide per
   card) instead of a static grid. CSS scroll-snap does the swipe/scroll physics natively
   (real touch momentum on mobile, no JS reimplementation needed); the JS below only handles
   the arrow buttons and keeping the dots in sync with whichever slide is in view. */
.news-section { margin: 3.5rem 0; }
.news-card, .beta-card {
  background: linear-gradient(160deg, var(--card), #16101f); border: 1px solid var(--border);
  border-radius: 10px; padding: 1.3rem 1.5rem; box-sizing: border-box; width: 100%; max-width: 560px;
}
.news-card h3, .beta-card h3 { color: var(--gold-bright); margin: 0 0 0.3rem; font-size: var(--fs-large); }
.news-card ul, .beta-card ul { margin: 0.8rem 0 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 0.7rem; }
.news-card li, .beta-card li { color: #d8d0e6; font-size: var(--fs-small); line-height: 1.4; }
.news-card .patch-date { color: var(--dim); font-size: var(--fs-micro); display: block; margin-bottom: 0.15rem; }
.beta-card {
  background: linear-gradient(160deg, #241a30, #16101f); border: 1px solid var(--gold);
  box-shadow: 0 10px 30px rgba(0,0,0,0.35);
}
.beta-card .beta-card-tag { color: var(--raven); font-size: var(--fs-micro); margin-bottom: 0.9rem; display: block; }

.news-carousel { margin: 1.5rem 0; max-width: 100%; }
.carousel-track {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  gap: 1.3rem; -webkit-overflow-scrolling: touch; scrollbar-width: none; -ms-overflow-style: none;
  /* Without an explicit width + min-width:0, a flex container that scrolls horizontally can
     size itself to its children's content (max-content) instead of its actual parent's
     width on mobile — each `flex: 0 0 100%` slide then resolves "100%" against that
     inflated content size instead of the real viewport, ballooning the whole page's layout
     width (confirmed: track.scrollWidth was 6100px+ on a 375px-wide phone) and forcing
     mobile browsers to zoom the entire site out to fit. */
  width: 100%; min-width: 0;
}
.carousel-track::-webkit-scrollbar { display: none; }
/* flex:0 0 100% + display:flex + justify-content:center centers the (max-width-capped)
   card WITHIN the slide's own box — centering it via margin:auto on the slide itself was
   the previous approach, but auto margins on a flex item's main axis absorb free space
   from that item's own flex-basis, silently inflating each slide's real scroll footprint
   to roughly the full container width regardless of gap, which desynced the scroll-snap
   positions IntersectionObserver expects from the visible card boundaries. */
.carousel-slide { flex: 0 0 100%; min-width: 0; scroll-snap-align: start; display: flex; justify-content: center; }
.carousel-controls { display: flex; align-items: center; justify-content: center; gap: 1.1rem; margin-top: 1.1rem; }
.carousel-arrow {
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0; cursor: pointer;
  border: 2px solid #6b4f26; color: var(--gold-bright); font-size: var(--fs-large); line-height: 1;
  background-image: linear-gradient(160deg, rgba(28,20,38,0.86), rgba(14,10,20,0.93)), url('/assets/lore/metal_texture.webp');
  background-size: cover, 260px 260px; background-position: center, 20% 30%; background-blend-mode: normal, luminosity;
  box-shadow: inset 0 1px 0 rgba(255,224,160,0.22), inset 0 -3px 5px rgba(0,0,0,0.6), 0 3px 7px rgba(0,0,0,0.45);
}
.carousel-arrow:hover { border-color: var(--gold); color: var(--gold-bright); }
/* O ponto continua com 9px pintados, mas o alvo tem 41. `background-clip:
   content-box` faz o preenchimento parar na caixa de conteudo, entao o padding
   e area clicavel invisivel -- um alvo de 9px num celular e um controle que so
   funciona por sorte. A margem negativa devolve o espaco que o padding tomaria. */
.carousel-dots { display: flex; gap: 0.55rem; margin: -16px 0; }
.carousel-dot {
  width: 9px; height: 9px; box-sizing: content-box; padding: 16px; margin: 0 -8px;
  border: none; border-radius: 50%; cursor: pointer;
  background: var(--border-lit); background-clip: content-box;
  transition: background 0.2s, transform 0.2s;
}
.carousel-dot:hover { background: var(--dim); background-clip: content-box; }
.carousel-dot.active { background: var(--gold); background-clip: content-box; transform: scale(1.35); }

/* Homepage world map — a hand-painted parchment map of Eldervast with all ten regions
   labeled directly on the illustration, framed like an aged, ink-bordered chart. */
.world-map-frame {
  position: relative; border: 2px solid var(--gold); border-radius: 8px; overflow: hidden; margin: 1.5rem 0 1.2rem;
  box-shadow: 0 0 0 1px var(--border-lit), 0 16px 40px rgba(0,0,0,0.55);
}
.world-map-img { width: 100%; height: auto; display: block; }
/* Each region is just its name, positioned over its own spot on the map (centroid of the
   same traced silhouette used previously) — no cutout image, no clip-path, no rectangle.
   At rest it reads as a label on the map; on hover it lifts and a highlight pill appears
   behind the text, the same "raised, selected" affordance the old image cutouts had. */
.region-label {
  position: absolute; transform: translate(-50%, -50%);
  display: inline-block; white-space: nowrap; cursor: pointer; z-index: 4;
  font-family: 'Cinzel', 'Georgia', serif; font-weight: 600;
  /* The map image is width:100% of its frame, so it shrinks with the viewport — but this
     label sat at a fixed 1.05rem regardless, reading as huge and disconnected from its
     region once the map itself got small on a phone. clamp()+vw ties it to viewport width
     instead: above ~646px it's still exactly 1.05rem (desktop untouched), below that it
     scales down with the map. */
  font-size: clamp(0.62rem, 2.6vw, 1.05rem);
  letter-spacing: 0.02em; color: var(--gold-bright); text-decoration: none;
  padding: clamp(0.15rem, 0.6vw, 0.35rem) clamp(0.4rem, 1.8vw, 1rem);
  border-radius: 999px; background: rgba(10,8,16,0.001);
  text-shadow: 0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.7);
  transition: transform 0.25s var(--ease), background 0.25s, box-shadow 0.25s, color 0.2s;
}
.region-label:hover, .region-label:focus-visible {
  transform: translate(-50%, -50%) translateY(-6px) scale(1.08);
  z-index: 5; outline: none; color: #fff;
  background: rgba(20, 16, 30, 0.88);
  box-shadow: 0 0 0 1px var(--gold), 0 10px 26px rgba(0,0,0,0.55), 0 0 20px rgba(201,162,74,0.5);
}
.region-label:active { transform: translate(-50%, -50%) translateY(-2px) scale(1.03); }
/* Mobile fallback: a grid of real buttons (same bronze-plate texture/bevel language as the
   sidebar nav buttons), hidden on desktop. Shrinking region-label's font alone wasn't enough
   — on a narrow map, two nearby regions' labels (e.g. Eldenwood/The Black Mire) still
   overlapped, since each is just placed at a fixed centroid with no collision check against
   its neighbors. Below the breakpoint the on-map labels hide entirely and this grid (in the
   same in-game level order, numbered) takes over as the way to reach each region page. */
.region-btn-grid { display: none; }
@media (max-width: 820px) {
  .region-label { display: none; }
  .region-btn-grid {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.65rem; margin: 0 0 1.8rem;
  }
  .region-btn {
    position: relative; display: flex; align-items: center; gap: 0.6rem;
    padding: 0.7rem 0.8rem; border-radius: 8px; overflow: hidden; text-decoration: none;
    border: 2px solid #6b4f26;
    background-image: linear-gradient(160deg, rgba(28,20,38,0.86), rgba(14,10,20,0.93)), url('/assets/lore/metal_texture.webp');
    background-size: cover, 260px 260px; background-position: center, 20% 30%; background-blend-mode: normal, luminosity;
    box-shadow:
      inset 0 1px 0 rgba(255,224,160,0.22), inset 0 -3px 5px rgba(0,0,0,0.6),
      inset 2px 0 0 rgba(255,215,140,0.06), 0 3px 7px rgba(0,0,0,0.45);
    transition: color 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
  }
  .region-btn:active {
    transform: translateY(1px);
    box-shadow: inset 0 2px 6px rgba(0,0,0,0.7), inset 0 -1px 0 rgba(255,224,160,0.1);
  }
  .region-btn-num {
    flex-shrink: 0; width: 1.9em; height: 1.9em; display: flex; align-items: center; justify-content: center;
    border-radius: 50%; border: 1.5px solid #8a6420; color: var(--gold-bright);
    font-family: 'Cinzel', serif; font-weight: 700; font-size: var(--fs-micro);
    background: rgba(0,0,0,0.25); text-shadow: 0 1px 1px rgba(0,0,0,0.9);
  }
  .region-btn-body { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
  .region-btn-name {
    font-family: 'Cinzel', 'Georgia', serif; font-weight: 600; font-size: var(--fs-small); letter-spacing: 0.02em;
    color: #d8cbe8; text-shadow: 0 1px 1px rgba(0,0,0,0.9), 0 -1px 0 rgba(255,224,160,0.12);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .region-btn-levels { color: var(--gold); font-size: var(--fs-micro); font-weight: 600; }
  .region-btn:hover .region-btn-name, .region-btn:active .region-btn-name { color: var(--gold-bright); }
}
@media (max-width: 480px) {
  .region-btn-grid { grid-template-columns: 1fr; }
}
/* width:100% (not just max-width + auto-sizing) is required here — main is a flex item
   whose width would otherwise be computed via shrink-to-fit, and a horizontally-scrolling
   descendant deep inside it (the news carousel) contributes its full unscrolled content
   width to that shrink-to-fit calculation despite its own overflow-x:auto, ballooning main
   itself to 1000px+ wide on a 375px phone instead of stretching to the real available
   width. An explicit width is a definite size and is immune to content-based sizing — the
   carousel is left free to scroll its own overflow internally, exactly as intended, instead
   of dragging documentElement.scrollWidth (and the whole page's mobile zoom level) with it. */
main { width: 100%; max-width: 1100px; min-width: 0; margin: 0 auto; padding: 2.5rem 2rem 5rem; position: relative; z-index: 1; box-sizing: border-box; }
h1 { color: var(--gold-bright); font-size: var(--fs-title); margin-bottom: 0.3rem; letter-spacing: 0.02em; }
h2 {
  color: var(--gold-bright); margin-top: 3rem; font-size: var(--fs-section); letter-spacing: 0.03em;
  display: flex; align-items: center; gap: 0.8rem;
}
h2::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, var(--border-lit), transparent); }
h3 { color: #d8c9e8; font-weight: 600; }
.subtitle { color: var(--dim); font-style: italic; margin-bottom: 2rem; font-size: var(--fs-large); line-height: 1.55; }
/* Banner for a documented-but-not-yet-live feature. Gold-bordered rather than a warning red:
   this is an announcement, not an error. */
/* Era uma barra de 4px na borda esquerda -- o acento colorido lateral e um
   maneirismo de UI generica, e este mundo tem um recurso proprio para "isto e um
   anuncio": a mesma placa de bronze biselada dos botoes do menu. */
.coming-soon {
  border: 2px solid var(--brass-dark); border-radius: 7px;
  padding: 1.1rem 1.3rem; margin: 0 0 2rem;
  background-image: linear-gradient(160deg, rgba(38,28,50,0.86), rgba(16,12,22,0.93)),
    url('/assets/lore/metal_texture.webp');
  background-size: cover, 260px 260px; background-position: center, 20% 30%;
  background-blend-mode: normal, luminosity;
  box-shadow: inset 0 1px 0 rgba(255,224,160,0.22), inset 0 -3px 5px rgba(0,0,0,0.6),
    0 3px 7px rgba(0,0,0,0.45);
}
.coming-soon b { color: var(--gold-bright); }
.coming-soon p { margin: 0.6rem 0 0; }
.coming-soon p:last-child { margin-bottom: 0; }

/* Homepage hero banner — a modest, normal-sized strip. The world art is now the fixed
   sitewide page background (see `body`), so the banner itself no longer carries its own
   background image; it's just a slightly taller header area for the logo + CTA. Busts out
   of `main`'s own padding only (not the full viewport — there's a permanent sidebar now, so
   the old left:50%/-50vw trick would run underneath it and misalign). */
.banner {
  width: calc(100% + 4rem); margin: -2.5rem -2rem 2.5rem; padding: 2.6rem 2rem; text-align: center;
  border-bottom: 1px solid var(--border-lit);
  background: linear-gradient(180deg, rgba(10,8,16,0.15), rgba(10,8,16,0.55));
}
.banner .banner-inner { position: relative; z-index: 1; }
.banner h1 { font-size: var(--fs-hero); text-shadow: 0 3px 20px rgba(0,0,0,0.8); margin-bottom: 0.6rem; }
/* display:block + margin:auto (not just the parent's text-align:center) — an <img> is inline
   by default, and on pages wide enough to leave room beside its max-width, the next element
   (the online badge, or on Home the subtitle) would float up next to it instead of wrapping
   onto its own line below, since nothing forced a line break. */
.banner .banner-logo {
  display: block; width: 100%; max-width: 340px; height: auto; margin: 0 auto 0.8rem;
  filter: drop-shadow(0 8px 30px rgba(0,0,0,0.7));
}
.banner .subtitle { max-width: 640px; margin: 0 auto; color: #e4dcf0; text-shadow: 0 2px 12px rgba(0,0,0,0.9); }

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1.3rem; margin: 1.5rem 0; }
.card {
  position: relative; background: linear-gradient(160deg, var(--card), #16101f); border: 1px solid var(--border);
  border-radius: 8px; overflow: hidden; text-decoration: none; color: var(--text);
  transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
}
.card:hover { border-color: var(--raven); transform: translateY(-3px); box-shadow: 0 10px 30px rgba(0,0,0,0.4); }
.card img { width: 100%; aspect-ratio: 1/1; object-fit: cover; display: block; filter: saturate(1.05) contrast(1.03);
  transition: transform 0.35s; }
.card:hover img { transform: scale(1.12); }
/* A soft diagonal gold glint sweeping across the card on hover — same light-catching-metal
   language as the nav buttons, applied subtly here since these are content cards, not CTAs. */
.card::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0;
  background: linear-gradient(115deg, transparent 42%, rgba(232,200,118,0.10) 50%, transparent 58%);
  transition: opacity 0.3s;
}
.card:hover::before { opacity: 1; }

/* Collapsible catalogue sections. A shut summary still states its count, so the page reads as
   a table of contents rather than as a page with its content missing.

   The chevron is a literal char, NOT a "B8" CSS escape: CSS lives in a plain (non-raw)
   Python string here, so Python eats the backslash as an octal escape and the browser is handed
   a control character followed by the text "B8". That shipped once and it looked exactly as bad
   as it sounds. */
.fold { margin: 0.35rem 0; }
.fold > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 0.7rem;
  padding: 0.85rem 1rem; border-radius: 10px;
  border: 1px solid var(--border);
  background: linear-gradient(160deg, rgba(46,37,64,0.5), rgba(10,8,16,0.35));
  color: var(--gold-bright); font-size: var(--fs-large); letter-spacing: 0.04em;
  font-family: 'Cinzel', 'Georgia', serif;
  transition: border-color 0.15s, background 0.15s;
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before {
  content: "▸"; color: var(--gold); font-size: 0.75em; line-height: 1;
  transition: transform 0.15s ease-out; display: inline-block;
}
.fold[open] > summary::before { transform: rotate(90deg); }
.fold > summary:hover { border-color: var(--border-lit); background: linear-gradient(160deg, rgba(74,58,102,0.45), rgba(10,8,16,0.35)); }
.fold[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.fold-count {
  margin-left: auto; color: var(--dim); font-size: var(--fs-micro);
  font-family: 'Spectral', Georgia, serif; letter-spacing: 0;
}

/* One nesting level in: same shape, quieter, and laid out in a row so six classes read as six
   chips instead of six full-width bars stacked down the page. */
.fold:not(.fold-sub) > .fold-body {
  display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0.6rem;
  border: 1px solid var(--border); border-top: none; border-radius: 0 0 10px 10px;
}
.fold:not(.fold-sub) > .fold-body > table { flex: 1 1 100%; margin: 0; }
.fold-sub > .fold-body { padding: 0.35rem 0 0.2rem; }
.fold-sub { margin: 0; flex: 1 1 12rem; }
.fold-sub > summary {
  padding: 0.5rem 0.75rem; border-radius: 8px; font-size: var(--fs-micro);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--text);
  background: rgba(46,37,64,0.35);
}
.fold-sub[open] { flex-basis: 100%; }
.fold-sub[open] > summary { color: var(--gold-bright); border-radius: 8px 8px 0 0; }
.fold-sub > table { margin: 0 0 0.2rem; }

/* Pixel-art item icons — scale up in place on hover, pixelated so it stays crisp instead of blurring */
.icon-zoom { display: inline-block; position: relative; vertical-align: middle; margin-right: 0.5rem; }
.icon-zoom img {
  width: 32px; height: 32px; border-radius: 3px; display: block;
  image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges;
  transition: transform 0.15s ease-out; position: relative; z-index: 1;
}
.icon-zoom:hover img {
  transform: scale(3.2); z-index: 20; box-shadow: 0 8px 24px rgba(0,0,0,0.6); border-radius: 5px;
  background: var(--bg2); outline: 2px solid var(--gold);
}
.card .card-body { padding: 0.9rem 1.1rem; }
.card .card-title { color: var(--gold-bright); font-size: var(--fs-body); }
.card .card-meta { color: var(--dim); font-size: var(--fs-small); }

/* Fallback square for cards whose item has no dedicated art yet (e.g. Wandering Merchant
   amulets) — same footprint as .card img so the grid stays visually consistent. */
.card .card-icon {
  width: 100%; aspect-ratio: 1/1; display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 50% 40%, var(--bg2), #0d0a14);
}
.card .card-icon svg { width: 56px; height: 56px; }
.card:hover .card-icon svg { transform: scale(1.12); transition: transform 0.35s; }

/* Player-lookup search box (Find Player page) — same input+button pattern reused wherever a
   free-text query hits the live stats API (bot/api/main.py). */
.lookup-box { display: flex; gap: 0.6rem; margin: 1.5rem 0 2rem; flex-wrap: wrap; }
.lookup-box input {
  flex: 1; min-width: 220px; background: var(--bg2); border: 1px solid var(--border-lit);
  border-radius: 8px; color: var(--text); padding: 0.7rem 1rem; font-size: var(--fs-body);
  /* A face do corpo, não uma terceira. Este era o único controle do site em EB
     Garamond, sobra de antes de o corpo virar Spectral. */
  font-family: 'Spectral', 'Georgia', serif;
}
.lookup-box input:focus { outline: none; border-color: var(--gold); }
.lookup-box button {
  background: var(--gold); color: #2e1f08; border: none; border-radius: 8px;
  padding: 0.7rem 1.4rem; font-weight: 700; cursor: pointer; font-family: 'Cinzel', serif;
  transition: background 0.15s;
}
.lookup-box button:hover { background: var(--gold-bright); }

/* Small live "N players online" indicator — fetched client-side from bot/api/main.py since
   the wiki itself is a static build, it can't know this at generation time.
   `width: 100%` is the base (sidebar) look — its own full-width row under the sidebar logo.
   The home hero's badge overrides this via .hero-online below (see that rule for why). */
.online-badge {
  display: inline-flex; width: 100%; align-items: center; justify-content: center; gap: 0.5rem;
  color: var(--dim); font-size: var(--fs-small); margin-top: 0.9rem;
}
.online-dot {
  width: 8px; height: 8px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 8px #4ade80;
  flex-shrink: 0;
}
/* Home hero badge — a compact dark pill sitting right under the logo, not a full-width row
   of small text under the CTA button (the previous placement read as an afterthought caption
   rather than a live status the player would actually notice). */
.hero-online {
  width: auto; margin: 1rem 0 1.4rem; padding: 0.5rem 1.1rem; border-radius: 999px;
  background: rgba(10,8,16,0.6); border: 1px solid var(--border-lit);
  color: var(--text); font-size: var(--fs-body);
}
/* Same live indicator, shrunk to sit under the sidebar brand logo on every page (not just
   the home hero) — this one is always in view, top-left, no scrolling needed. Brighter than
   --dim + a slightly larger font: the muted color read as basically invisible against the
   near-black sidebar background at the original size. */
.sidebar-online {
  display: flex; width: 100%; align-items: center; justify-content: center; gap: 0.4rem;
  color: var(--text); font-size: var(--fs-micro); margin: 0 0 0.4rem; text-align: center;
}
.achievement-locked { opacity: 0.45; }

/* Character portraits — framed like a hand-painted canvas: inset border + vignette + soft glow */
.char-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1.8rem; margin: 1.5rem 0; }
.char-card {
  position: relative; display: block; border-radius: 4px; overflow: hidden; text-decoration: none;
  aspect-ratio: 3/4; border: 1px solid var(--gold); outline: 6px solid var(--bg2);
  box-shadow: 0 0 0 1px var(--border-lit), 0 12px 28px rgba(0,0,0,0.5);
  transition: box-shadow 0.2s, transform 0.2s, outline-color 0.2s;
}
.char-card:hover { box-shadow: 0 0 0 1px var(--raven), 0 0 30px rgba(138,118,201,0.35), 0 12px 28px rgba(0,0,0,0.5); transform: translateY(-3px); outline-color: #1c1628; }
.char-card img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(1.08) contrast(1.05); }
.char-card::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 40px 8px rgba(0,0,0,0.55); /* painterly vignette */
}
.char-card .plate {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 1rem 1.1rem 0.8rem; z-index: 1;
  background: linear-gradient(to top, rgba(6,4,10,0.95) 15%, rgba(6,4,10,0.7) 55%, transparent 100%);
}
.char-card .plate .name { color: var(--gold-bright); font-size: var(--fs-large); font-weight: 700; display: block; letter-spacing: 0.02em; }
.char-card .plate .order { color: #cfc3e0; font-size: var(--fs-small); display: block; margin-top: 0.2rem; font-style: italic; }

table { width: 100%; border-collapse: collapse; margin: 1rem 0 2rem; }
th, td { text-align: left; padding: 0.55rem 0.8rem; border-bottom: 1px solid var(--border); font-size: var(--fs-small); }
th { color: var(--gold-bright); font-weight: 600; border-bottom: 2px solid var(--border-lit); font-family: 'Cinzel', serif; font-size: var(--fs-micro); letter-spacing: 0.03em; text-transform: uppercase; }
tr:hover td { background: rgba(138, 118, 201, 0.07); }
.rarity-common { color: #b8b0c8; } .rarity-uncommon { color: #6fd18a; }
.rarity-rare { color: #6fa8d1; } .rarity-epic { color: #c06fd1; } .rarity-legendary { color: var(--gold-bright); }

.hero { display: flex; gap: 2.2rem; align-items: flex-start; flex-wrap: wrap; margin-bottom: 2rem; }
.hero img {
  width: 280px; border-radius: 6px; border: 1px solid var(--gold); outline: 5px solid var(--bg2);
  box-shadow: 0 0 0 1px var(--border-lit), 0 14px 34px rgba(0,0,0,0.5);
}
.hero.hero-lg img { width: 440px; max-width: 100%; }
.stat-row { display: flex; flex-wrap: wrap; gap: 0.6rem 1.5rem; margin: 1rem 0; }
.stat-row span { color: var(--dim); } .stat-row b { color: var(--text); }
/* Small bevel-button treatment for clickable tags (e.g. a map's creature list) — same
   bronze/gold-plate language as the sidebar nav, scaled down. */
.tag {
  display: inline-block; position: relative; background-image:
    linear-gradient(160deg, rgba(36,26,48,0.9), rgba(20,14,28,0.95)), url('/assets/lore/metal_texture.webp');
  background-size: cover, 200px 200px; background-blend-mode: normal, soft-light;
  border: 1.5px solid #6b4f26; border-radius: 5px;
  padding: 0.28rem 0.8rem; font-size: var(--fs-small); color: #cbb98a; margin: 0.15rem 0.3rem 0.15rem 0;
  text-decoration: none; letter-spacing: 0.02em;
  box-shadow: inset 0 1px 0 rgba(255,224,160,0.15), inset 0 -2px 3px rgba(0,0,0,0.5), 0 2px 5px rgba(0,0,0,0.35);
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
a.tag:hover {
  color: var(--gold-bright); border-color: var(--gold); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255,224,160,0.25), inset 0 -2px 3px rgba(0,0,0,0.5), 0 4px 10px rgba(0,0,0,0.45), 0 0 12px rgba(201,162,74,0.25);
}
footer { text-align: center; color: var(--dim); padding: 2.5rem; font-size: var(--fs-small); border-top: 1px solid var(--border); position: relative; z-index: 1; }
footer .feedback-link {
  display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 0.9rem; padding: 0.5rem 1.1rem;
  border-radius: 999px; border: 1px solid var(--border-lit); color: var(--text); text-decoration: none;
  font-size: var(--fs-small);
}
footer .feedback-link:hover { border-color: var(--gold); color: var(--gold-bright); }
/* "← Back" links — a small slide-toward-the-arrow micro-interaction instead of a flat
   color swap, consistent with the rest of the site's hover language. */
a.back {
  display: inline-block; color: var(--dim); text-decoration: none; font-size: var(--fs-small);
  font-family: 'Cinzel', serif; letter-spacing: 0.02em; padding-bottom: 2px;
  border-bottom: 1px solid transparent; transition: color 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
a.back:hover { color: var(--gold-bright); border-bottom-color: var(--gold); transform: translateX(-4px); }
