/* ==========================================================================
   THEME TOKENS
   Every color in this file reads from these custom properties. Switching
   themes just swaps the block below via [data-theme="..."] on <html> — no
   other rule needs to change. Add a new theme by adding a new block and an
   entry in the THEMES array in app.js.
   ========================================================================== */
:root, html[data-theme="infernal"]{
  --ink:#120f0b;
  --panel:#1c170f;
  --panel-2:#241d13;
  --iron:#453c2c;
  --iron-soft:#332b1f;
  --blood:#9c1c26;
  --blood-bright:#c2323d;
  --ember:#c98a45;
  --ember-dim:#8a5f2f;
  --bone:#e9dfc7;
  --ash:#a99d84;
  --ash-dim:#6f6650;
  --on-accent:#120f0b;
  --texture-line: rgba(255,255,255,0.012);
  --shadow: 0 10px 30px rgba(0,0,0,0.5);
}

/* Token de layout (não de cor -- por isso vive num bloco :root próprio,
   separado do bloco de cores por tema acima, e não varia por [data-theme]).
   ADR-076 § Decisão item 2: altura real de `.global-nav` (ADR-027,
   TASK-232), único ponto de verdade reaproveitado por qualquer overlay fixo
   (`.wizard-overlay`) que precise nunca ocupar essa faixa. */
:root{
  --nav-height: 56px;
}

html[data-theme="dark"]{
  --ink:#111317;
  --panel:#181b20;
  --panel-2:#1f232a;
  --iron:#3a4048;
  --iron-soft:#282d34;
  --blood:#3f6fb0;
  --blood-bright:#5b93d6;
  --ember:#9fb4c7;
  --ember-dim:#6f8395;
  --bone:#e8eaed;
  --ash:#a7adb6;
  --ash-dim:#6b7178;
  --on-accent:#0f1216;
  --texture-line: rgba(255,255,255,0.012);
  --shadow: 0 10px 30px rgba(0,0,0,0.55);
}

html[data-theme="light"]{
  --ink:#f3f1ec;
  --panel:#ffffff;
  --panel-2:#f8f6f1;
  --iron:#ddd8cc;
  --iron-soft:#e9e5da;
  --blood:#b3392f;
  --blood-bright:#c9463a;
  --ember:#a6762c;
  --ember-dim:#c99a53;
  --bone:#241f18;
  --ash:#5b5346;
  --ash-dim:#8b8271;
  --on-accent:#fffaf0;
  --texture-line: rgba(20,15,5,0.035);
  --shadow: 0 6px 22px rgba(60,50,30,0.12);
}

html[data-theme="classic"]{
  --ink:#e7d9ac;
  --panel:#f2e6bd;
  --panel-2:#ecdda8;
  --iron:#c2a968;
  --iron-soft:#d9c78e;
  --blood:#7a1f1f;
  --blood-bright:#962b2b;
  --ember:#8a5a2b;
  --ember-dim:#a97b3f;
  --bone:#2c2113;
  --ash:#4c3c22;
  --ash-dim:#6f5c37;
  --on-accent:#faf1d6;
  --texture-line: rgba(90,60,20,0.05);
  --shadow: 0 8px 22px rgba(60,40,10,0.22);
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  background:
    radial-gradient(1200px 600px at 15% -10%, color-mix(in srgb, var(--blood) 10%, transparent), transparent 60%),
    radial-gradient(900px 500px at 100% 10%, color-mix(in srgb, var(--ember) 6%, transparent), transparent 60%),
    var(--ink);
  color:var(--bone);
  font-family:'Crimson Pro', serif;
  min-height:100vh;
  -webkit-font-smoothing:antialiased;
  transition: background-color .25s ease, color .25s ease;
}
.texture{
  position:fixed; inset:0; pointer-events:none; z-index:0; opacity:0.5;
  background-image:
    repeating-linear-gradient(0deg, var(--texture-line) 0px, var(--texture-line) 1px, transparent 1px, transparent 3px);
}

.wrap{ position:relative; z-index:1; max-width:1180px; margin:0 auto; padding:28px 20px 80px; }

/* ---------- Character switcher ---------- */
.switcher{
  display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap;
  margin-bottom:16px;
}
/* TASK-229 (UC-045 ext., ADR-050 § Adendo): `position:relative` ancora
   `#sheet-actions-more-group` (gatilho único "Ações do personagem", abaixo). */
.switcher-right{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; position:relative; }

.theme-switcher{ display:flex; align-items:center; gap:10px; }
.theme-switcher .ts-label{
  font-family:'JetBrains Mono', monospace; font-size:11px; letter-spacing:0.14em;
  text-transform:uppercase; color:var(--ash-dim);
}
.theme-switcher .ts-buttons{ display:flex; gap:7px; }
.theme-btn{
  width:30px; height:30px; border-radius:50%; padding:0; cursor:pointer;
  border:2px solid var(--iron-soft); background:var(--swatch, var(--ember));
  transition:transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.theme-btn:hover{ transform:translateY(-2px); }
.theme-btn.active{
  border-color:var(--panel);
  box-shadow: 0 0 0 2px var(--ember);
}
.theme-btn:focus-visible{ outline:2px solid var(--ember); outline-offset:2px; }

/* TASK-271 (UC-014 "décima sexta frente", ADR-064 § Decisão item 3): seletor de idioma na página
   de Preferences -- `.theme-btn` não é reaproveitável (seu estilo depende de `--swatch`, sem
   sentido para um rótulo de idioma), então este é um par novo e pequeno, autocontido. */
.locale-btn{
  padding:8px 14px; border-radius:20px; border:1px solid var(--iron); background:var(--panel-2);
  color:var(--ash); font-family:'JetBrains Mono', monospace; font-size:13px; cursor:pointer;
}
.locale-btn.active{ border-color:var(--ember); color:var(--bone); }
.locale-btn:focus-visible{ outline:2px solid var(--ember); outline-offset:2px; }

/* ---------- Sheet action buttons (lock / save / level up / export / share / …) ---------- */
.icon-btn{
  display:inline-flex; align-items:center; gap:7px;
  background:var(--panel); color:var(--bone); border:1px solid var(--iron);
  border-radius:5px; padding:8px 14px; font-family:'Cinzel', serif; font-weight:600;
  font-size:12.5px; letter-spacing:0.03em; cursor:pointer; transition:all .15s ease;
}
.icon-btn:hover{ border-color:var(--ember-dim); color:var(--ember); }
.icon-btn#lock-toggle.unlocked{ border-color:var(--blood-bright); color:var(--blood-bright); }
.icon-btn.save-btn{ background:var(--ember); color:var(--on-accent); border-color:var(--ember); }
.icon-btn.save-btn:hover{ filter:brightness(1.08); color:var(--on-accent); }
.icon-btn.save-btn:disabled{ opacity:0.45; cursor:not-allowed; filter:none; }
/* TASK-157 (UC-032): botão "Exportar/Imprimir" -- mesmo tratamento visual de
   desabilitado (escopado por id, mesma justificativa de #level-up-btn/
   #delete-character-btn, styles.css abaixo) para quando nenhum personagem
   está carregado. */
#export-print-btn:disabled{ opacity:0.45; cursor:not-allowed; }
/* TASK-162 (UC-033, ADR-039 § Decisão itens 3/6): toggle de compartilhamento público +
   linha de URL copiável -- mesmo tratamento de desabilitado de #export-print-btn/
   #delete-character-btn acima; `.public` (aplicada por sheet.js::updateShareUI) destaca
   o botão quando `state.currentSource.public === true`, mesmo espírito visual de
   `.icon-btn#lock-toggle.unlocked` (cor diferente, para não sobrepor o significado de
   "destravado para edição"). */
#share-toggle-btn:disabled{ opacity:0.45; cursor:not-allowed; }
.icon-btn#share-toggle-btn.public{ border-color:var(--ember); color:var(--ember); }
.share-url-row{ display:flex; align-items:center; gap:6px; }
.share-url-field{
  width:220px; font-family:'JetBrains Mono', monospace; font-size:12px; padding:6px 8px;
  cursor:text;
}
.save-status{
  font-family:'JetBrains Mono', monospace; font-size:11.5px; color:var(--ash-dim);
  transition:opacity .3s ease;
}
.save-status.error{ color:var(--blood-bright); }

/* TASK-229 (UC-045 ext., ADR-050 § Adendo 2026-08-17): disclosure único de `.switcher-right`,
   substitui `.edit-controls-more-group` (ADR-050 original/TASK-213, só Export/Share) -- agora
   reúne as sete ações (Level Up, Add Class, Apagar, Editar, Salvar, Export/Print, Compartilhar).
   Mesma receita visual, sem `@media` (fechado por padrão em QUALQUER largura, ADR-050 § Decisão
   item 2, mantida pela emenda). Ancorado à direita (`right:0`) -- `.switcher-left` não tem mais
   nenhum disclosure próprio para colidir com este (`#switcher-more-group`/ADR-042 deixou de
   existir), mas a âncora à direita permanece a mais natural para um gatilho de `.switcher-right`. */
.sheet-actions-more-group{
  display:none; flex-direction:column; gap:8px;
  position:absolute; top:calc(100% + 6px); right:0; z-index:50;
  min-width:220px; padding:10px;
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border:1px solid var(--iron); border-radius:6px; box-shadow:var(--shadow);
}
.sheet-actions-more-group.open{ display:flex; }
.sheet-actions-more-group .icon-btn{ width:100%; justify-content:flex-start; }

/* ---------- Editable fields ---------- */
.efield, .efield-num{
  width:100%; background:var(--ink); color:var(--bone); border:1px solid var(--iron-soft);
  border-radius:4px; padding:6px 8px; font-family:'Crimson Pro', serif; font-size:14.5px;
}
.efield-num{ font-family:'JetBrains Mono', monospace; text-align:center; }
.efield:focus, .efield-num:focus{ outline:2px solid var(--ember); outline-offset:1px; }
textarea.efield{ resize:vertical; min-height:64px; line-height:1.5; font-style:italic; }
.efield-label{
  display:block; font-family:'JetBrains Mono', monospace; font-size:10px; letter-spacing:0.1em;
  text-transform:uppercase; color:var(--ash-dim); margin-bottom:5px;
}
.efield-group{ margin-bottom:2px; }

.dot-btn{ background:none; border:none; padding:0; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; width:14px; height:14px; flex-shrink:0; }
.dot.expert{ background:var(--ember); border-color:var(--ember); box-shadow:0 0 0 2px var(--ink), 0 0 0 3px var(--blood-bright); }

.add-btn{
  display:inline-flex; align-items:center; gap:6px;
  background:transparent; border:1px dashed var(--iron); color:var(--ash);
  border-radius:5px; padding:8px 14px; font-family:'Cinzel', serif; font-size:12.5px;
  font-weight:600; letter-spacing:0.05em; cursor:pointer; width:100%; justify-content:center;
  transition:all .15s ease;
}
.add-btn:hover{ border-color:var(--ember); color:var(--ember); }
.remove-btn{
  background:none; border:1px solid var(--iron-soft); color:var(--ash-dim);
  border-radius:4px; width:24px; height:24px; line-height:1; cursor:pointer; flex-shrink:0;
  font-size:14px; transition:all .15s ease;
}
.remove-btn:hover{ border-color:var(--blood-bright); color:var(--blood-bright); }

/* TASK-247 (UC-054/ADR-058 § Decisão item 6): icon button that publishes a content block to the
   room chat (`buildRoomChatShareButton`, sheet.js -- used inside `.acc-head-title-row`,
   `.spell-head-right`, and `.oath-spell-chip`, all gated by `currentRoomId`). Same visual spirit
   as `.remove-btn` above (thin border, transparent background, no emphasis until hover) but an
   accent hover color (`--ember`) rather than a destructive one (`--blood-bright`) -- publishing to
   the room chat isn't a destructive action. */
.room-chat-share-btn{
  background:none; border:1px solid var(--iron-soft); color:var(--ash-dim);
  border-radius:4px; width:24px; height:24px; padding:0; flex-shrink:0; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; transition:all .15s ease;
}
.room-chat-share-btn svg{ width:14px; height:14px; }
.room-chat-share-btn:hover{ border-color:var(--ember-dim); color:var(--ember); }

/* TASK-313 (UC-061/ADR-077): `.eq-edit-row`/seu `.efield-qty` deixaram de existir --
   renderEquipment() não renderiza mais `<li class="eq-edit-row">` com <input>s inline, ver
   `.attack-modal`/`.eq-edit-btn`/`.eq-row-compact` abaixo. */
.eq-edit-row{ display:flex; align-items:center; gap:6px; padding:6px 0; border-bottom:1px dashed var(--iron-soft); }
.eq-edit-row:last-child{ border-bottom:none; }
.eq-edit-row .efield{ flex:1 1 auto; }
.eq-edit-row .efield-qty{ width:52px; flex:0 0 52px; }

/* TASK-304 (UC-059/ADR-073): `.attack-edit-row`/seu `.efield` inline deixaram de existir --
   renderAttacks() não renderiza mais `<tr class="attack-edit-row">` com <input>s, ver
   `.attack-modal`/`.attack-edit-btn` acima. */
.edit-only-col{ width:34px; }

.feature-edit{ padding:14px 18px 16px; }
.feature-edit .efield-group + .efield-group{ margin-top:10px; }
.feature-edit-toprow{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:2px; }

.tenet-edit{ margin-bottom:14px; padding-bottom:14px; border-bottom:1px solid var(--iron-soft); }
.tenet-edit:last-child{ border-bottom:none; margin-bottom:0; padding-bottom:0; }
.tenet-edit .efield-group + .efield-group{ margin-top:8px; }
.tenet-edit-toprow{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
/* TASK-229 (UC-045 ext., ADR-050 § Adendo): `.switcher-left` simplificada -- só
   `#character-select`(+`#new-character-btn` em index.html) diretos, sem disclosure próprio
   (`#switcher-more-btn`/`#switcher-more-group`, ADR-042, deixou de existir -- Level Up/Add
   Class/Apagar migraram para o disclosure único de `.switcher-right`). `flex-wrap:wrap` evita
   overflow horizontal em viewport estreito mesmo com só estes dois controles. */
.switcher-left{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; position:relative; }
.switcher label{
  font-family:'JetBrains Mono', monospace; font-size:11px; letter-spacing:0.14em;
  text-transform:uppercase; color:var(--ash-dim);
}
.switcher select{
  background:var(--panel); color:var(--bone); border:1px solid var(--iron);
  border-radius:5px; padding:9px 14px; font-family:'Cinzel', serif; font-weight:600;
  font-size:13px; letter-spacing:0.04em; cursor:pointer; min-width:220px;
}
.switcher select:focus{ outline:2px solid var(--ember); outline-offset:2px; }

@media (max-width:420px){
  /* Seletor perde o piso fixo de 220px (impossível de caber em qualquer
     celular comum) em favor de ocupar o espaço disponível na linha em que
     cair, dentro de `.switcher-left{flex-wrap:wrap}` acima -- mesmo padrão
     recomendado por ADR-042 § Decisão item 1. */
  .switcher select{ min-width:0; flex:1 1 auto; }
}

/* ---------- Generic visibility utility (auth-screen/app-screen toggling, TASK-006) ---------- */
.hidden{ display:none !important; }

/* ---------- Auth screen (login/register, TASK-006, UC-001) ---------- */
.auth-screen{
  position:relative; z-index:1; min-height:100vh;
  display:flex; align-items:center; justify-content:center; padding:28px 20px;
}
.auth-panel{ width:100%; max-width:380px; }
.auth-form .efield-group{ margin-bottom:14px; }
.auth-submit{ width:100%; justify-content:center; margin-top:4px; }
.auth-switch{
  margin:16px 0 0; text-align:center; font-size:13px; color:var(--ash-dim);
}
.auth-switch a{ color:var(--ember); text-decoration:none; }
.auth-switch a:hover{ text-decoration:underline; }
.form-error{
  color:var(--blood-bright); font-size:13px; line-height:1.5; margin:-4px 0 12px;
}

/* ---------- Landing screen (Visitante, TASK-231, UC-052, ADR-056) ----------
   Terceira "tela" da SPA, mesmo espírito estrutural de .auth-screen acima -- position:relative,
   min-height:100vh, sem overlay/position:fixed. A alternância com #auth-screen/#app-screen é só
   por .hidden (auth-ui.js), nunca duas ao mesmo tempo. Mobile-first: .landing-features nasce em
   1 coluna, só ganha 3 colunas a partir de 640px. */
.landing-screen{
  position:relative; z-index:1; min-height:100vh;
  display:flex; align-items:center; justify-content:center; padding:48px 20px;
}
.landing-hero{ width:100%; max-width:640px; text-align:center; }
.landing-logo{
  display:block;
  height:clamp(72px, 40vw, 300px);
  width:auto;
  margin:0 auto 18px;
}
.landing-eyebrow{
  font-family:'JetBrains Mono', monospace; font-size:11px; letter-spacing:0.24em;
  text-transform:uppercase; color:var(--ember); margin-bottom:14px;
}
.landing-title{
  font-family:'Cinzel Decorative', serif; font-weight:900;
  font-size:clamp(38px, 9vw, 64px); letter-spacing:0.04em; margin:0 0 16px;
  color:var(--bone); text-shadow:0 2px 0 rgba(0,0,0,0.4);
}
.landing-tagline{
  font-family:'Crimson Pro', serif; font-size:clamp(15px, 2.4vw, 18px); line-height:1.6;
  color:var(--ash); margin:0 0 32px;
}
.landing-cta{ padding:14px 36px; font-size:14px; letter-spacing:0.06em; }
.landing-features{
  display:grid; grid-template-columns:1fr; gap:14px; margin-top:44px; text-align:left;
}
@media (min-width:640px){
  .landing-features{ grid-template-columns:repeat(3, 1fr); }
}
.landing-feature{
  border:1px solid var(--iron); border-radius:6px; padding:16px 18px;
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
}
.landing-feature-icon{ font-size:22px; line-height:1; margin-bottom:8px; }
.landing-feature-title{
  font-family:'Cinzel', serif; font-weight:600; font-size:14px; color:var(--bone); margin:0 0 6px;
}
.landing-feature-desc{ font-size:13px; line-height:1.5; color:var(--ash-dim); margin:0; }

/* ---------- Header ---------- */
header.sheet-header{
  border:1px solid var(--iron);
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border-radius:6px;
  padding:28px 30px 22px;
  position:relative;
  overflow:hidden;
  box-shadow:var(--shadow);
}
header.sheet-header::before{
  content:"";
  position:absolute; inset:0;
  background:
    linear-gradient(90deg, var(--blood) 0%, var(--blood) 4px, transparent 4px),
    linear-gradient(270deg, var(--blood) 0%, var(--blood) 4px, transparent 4px);
  pointer-events:none;
}
.header-grid{ display:flex; align-items:flex-end; justify-content:space-between; gap:24px; flex-wrap:wrap; }
/* TASK-155 (UC-031, ADR-037): imagem do personagem -- `.header-identity` agrupa retrato+nome como
   um único filho de `.header-grid` (junto de `.header-right`), sem tocar no `display:flex;
   justify-content:space-between` já existente acima (continua exatamente 2 filhos de topo). */
.header-identity{ display:flex; align-items:center; gap:18px; flex-wrap:wrap; }
.header-portrait{ display:flex; flex-direction:column; align-items:center; gap:6px; flex-shrink:0; }
.portrait-frame{
  width:84px; height:84px; border-radius:6px; overflow:hidden; flex-shrink:0;
  border:1px solid var(--iron); background:var(--panel-2);
  display:flex; align-items:center; justify-content:center;
}
.portrait-img{ width:100%; height:100%; object-fit:cover; display:block; }
.portrait-placeholder{ font-size:36px; line-height:1; color:var(--ash-dim); }
.portrait-actions{ display:flex; gap:6px; }
.portrait-action-btn{ padding:4px 8px; font-size:11px; cursor:pointer; }
.portrait-action-btn.disabled, .portrait-action-btn:disabled{
  opacity:0.45; cursor:not-allowed; pointer-events:none;
}
.eyebrow{
  font-family:'JetBrains Mono', monospace;
  font-size:11px; letter-spacing:0.28em; text-transform:uppercase;
  color:var(--ember); margin-bottom:8px;
}
h1.charname{
  font-family:'Cinzel Decorative', serif;
  font-weight:900;
  font-size:clamp(30px, 4.4vw, 52px);
  letter-spacing:0.02em;
  margin:0 0 6px 0;
  color:var(--bone);
  text-shadow: 0 2px 0 rgba(0,0,0,0.4);
}
.subtitle{
  font-family:'Cinzel', serif; font-weight:600;
  font-size:15px; letter-spacing:0.06em; color:var(--ash);
  text-transform:uppercase;
}
.subtitle span.acc{ color:var(--blood-bright); }
.header-right{ display:flex; gap:28px; flex-wrap:wrap; }
.hstat{ text-align:center; min-width:64px; }
.hstat .n{
  font-family:'JetBrains Mono', monospace; font-weight:700;
  font-size:26px; color:var(--ember); line-height:1;
}
.hstat .l{
  font-family:'JetBrains Mono', monospace;
  font-size:10px; letter-spacing:0.14em; text-transform:uppercase; color:var(--ash-dim);
  margin-top:4px;
}

/* ---------- Nav tabs ---------- */
nav.tabs{
  display:flex; gap:2px; margin:22px 0 22px;
  border:1px solid var(--iron); border-radius:6px; overflow:hidden;
  background:var(--panel-2);
}
nav.tabs button{
  flex:1; padding:13px 10px;
  background:transparent; border:none; cursor:pointer;
  font-family:'Cinzel', serif; font-weight:600; font-size:13px;
  letter-spacing:0.1em; text-transform:uppercase;
  color:var(--ash); transition:all .2s ease;
  border-right:1px solid var(--iron-soft);
}
nav.tabs button:last-child{ border-right:none; }
nav.tabs button:hover{ color:var(--bone); background:color-mix(in srgb, var(--ember) 8%, transparent); }
nav.tabs button.active{ color:var(--on-accent); background:var(--ember); }

.tabpanel{ display:none; animation:fade .25s ease; }
.tabpanel.active{ display:block; }
/* Bug encontrado pelo code-reviewer durante a revisão de TASK-102/UC-016 (sem
   TASK própria -- fix pontual que bloqueava o fechamento de TASK-107): o
   widget flutuante de dados (`.dice-widget`, fixed, right:26px bottom:26px)
   fica sobreposto a controles interativos do fim de uma aba quando o
   conteúdo dessa aba termina perto o bastante do rodapé da janela. Só
   `.dice-fab-row` (dentro de `.dice-widget`) tem `pointer-events:auto` -- o
   resto do widget tem `pointer-events:none` -- então a zona de perigo real é
   essa fileira de botões, medida (viewport de referência 1280x1400, fontes
   já carregadas) em x:~1049-1254 / y:~1320-1374, não o container inteiro.
   Antes de TASK-102 a aba Features nunca era alta o bastante para isso
   acontecer; agora que um Fighter nasce com 2 class features automáticas
   (Fighting Style + Second Wind), o botão #feat-catalog-add-btn (painel
   Feats, o último da aba) caía nessa faixa.
   padding-bottom AQUI (depois do conteúdo) é só a folga de fim de documento
   para um usuário rolando manualmente além do último elemento -- não resolve
   a colisão em si (não move nenhum elemento em X nem garante nada sobre onde
   o botão cai relativo ao widget). Na época, a correção real da colisão era
   um `padding-right:220px` em `.tabpanel` -- removido por TASK-118 (UC-019,
   ADR-027): `.dice-widget` deixou de existir como elemento flutuante
   (conteúdo movido para dentro de `<header class="global-nav">`, barra fixa
   no topo), então a causa raiz da colisão (dois controles `position:fixed`
   na mesma região onde o fim de uma aba podia terminar) deixou de existir,
   sem precisar de um mecanismo equivalente. Este `padding-bottom` continua
   válido (folga de fim de documento, motivo independente da colisão).
   Histórico: uma tentativa anterior (`#feat-panel{ margin-top:150px; }`,
   revertida) tentava resolver isso empurrando o conteúdo verticalmente, o
   que só "enganava" o `scrollIntoViewIfNeeded` do Puppeteer -- o
   code-reviewer reproduziu manualmente (varredura de `scrollY` de 0 a
   415px nesse mesmo viewport, via `elementFromPoint`) uma janela real de
   colisão entre `scrollY≈103` e `scrollY≈209` onde o botão ainda ficava atrás
   do widget para um usuário rolando a página normalmente. A correção atual
   ataca o eixo X (que não muda com scroll) em vez do eixo Y. */
.tabpanel.active{ padding-bottom:140px; }
@keyframes fade{ from{opacity:0; transform:translateY(4px);} to{opacity:1; transform:translateY(0);} }

/* ---------- Panels / cards ---------- */
.panel{
  border:1px solid var(--iron); background:var(--panel); border-radius:6px;
  padding:20px; box-shadow:var(--shadow);
}
.panel + .panel{ margin-top:18px; }
/* Bug (2026-08-17, sem TASK numerada): `.panel + .panel` acima existe para separar painéis
   EMPILHADOS verticalmente, mas o combinador de irmão adjacente também disparava para dois
   `.panel` lado a lado dentro de `.grid.cols-2` (ex. "Saving Throws" + "Proficiencies &
   Languages") -- o segundo `.panel` da linha ganhava `margin-top:18px` DENTRO da própria célula
   do grid, deslocando seu topo 18px abaixo do primeiro mesmo com `align-items:stretch` (padrão
   do CSS Grid) esticando as duas células para a mesma altura de linha -- resultado: as duas
   caixas terminavam no mesmo Y (fundo alinhado), mas começavam em Y diferentes (topo
   desalinhado), com alturas visivelmente diferentes. Reseta aqui para qualquer `.panel` que seja
   item direto de `.grid` (cols-2/cols-3), preservando o espaçamento de 18px para o caso original
   de painéis empilhados fora de um grid. */
.grid > .panel + .panel{ margin-top:0; }
.panel-title{
  font-family:'Cinzel', serif; font-weight:700; font-size:14px;
  letter-spacing:0.14em; text-transform:uppercase; color:var(--ember);
  margin:0 0 16px 0; padding-bottom:10px; border-bottom:1px solid var(--iron-soft);
  display:flex; align-items:center; gap:10px;
}
.panel-title::before{
  content:""; width:10px; height:10px; background:var(--blood); transform:rotate(45deg);
  display:inline-block; flex-shrink:0;
}

.grid{ display:grid; gap:16px; }
.grid.cols-2{ grid-template-columns:1fr 1fr; }
.grid.cols-3{ grid-template-columns:repeat(3,1fr); }

@media (max-width:760px){
  .grid.cols-2, .grid.cols-3{ grid-template-columns:1fr; }
  nav.tabs{ flex-wrap:wrap; }
  nav.tabs button{ flex:1 1 45%; }
  /* TASK-207 (achado da suíte de fechamento E2E UC-039/040/041 -- primeira
     cobertura automatizada real de 320-760px nesta barra): o `.switcher`
     original em modo flex `column` empilhava `.switcher-left` (label+select+
     `#new-character-btn`) inteiro acima de `.switcher-right`
     (`#sheet-actions-more-btn`) inteiro, cada bloco como uma única "coluna"
     vertical -- o motivo de existir um modo `column` aqui em vez do row+wrap
     puro é histórico e permanece válido: em modo row, `flex-wrap:wrap`
     herdado da regra base (linha ~103) fazia o Chrome calcular a largura da
     linha por conteúdo NÃO quebrado, "vazando" `.switcher-left`/
     `.switcher-right` para até ~560px de largura e causando rolagem
     horizontal real em qualquer viewport onde a soma excedesse o espaço
     disponível (~320-700px).

     TASK-301 (ADR-072 § Decisão item 5): reprodução visual confirmou (
     Puppeteer, `getBoundingClientRect()`, 320-760px, index.html e room.html)
     que esse empilhamento em blocos deixava `#sheet-actions-more-btn`
     numa linha separada de `#new-character-btn`, alinhado à ESQUERDA -- não
     acompanhando `#new-character-btn` na mesma linha nem alinhado à direita,
     como já acontece em >=761px (`.switcher{display:flex;
     justify-content:space-between}` da regra base). Fix: `.switcher` passa a
     `display:grid` aqui; `.switcher-left` ganha `display:contents` (não gera
     caixa própria -- só afeta a árvore de RENDERIZAÇÃO, não a árvore DOM,
     então `.switcher-left.children` continua `[label, select,
     #new-character-btn]` para qualquer leitor de DOM/teste), promovendo
     label/select/`#new-character-btn` a itens diretos do grid. `label` e
     `select` ocupam a linha inteira (`grid-column:1/-1`) cada um; sobra só a
     coluna 1 da PRÓXIMA linha livre para `#new-character-btn` -- o
     auto-placement do grid (linha a linha, esquerda->direita) encontra a
     coluna 2 dessa mesma linha livre em seguida, exatamente onde
     `.switcher-right` (não promovido -- continua uma caixa normal, mantendo
     `position:relative` como âncora inalterada de `.sheet-actions-more-group`)
     cai por ordem de DOM. Resultado: os dois botões acabam na mesma linha,
     `#new-character-btn` à esquerda e `#sheet-actions-more-btn` à direita da
     própria largura de `.switcher` -- mesmo espírito do layout >=761px,
     agora também abaixo de 760px. Sem overflow horizontal 320-1024px (mesmo
     critério de UC-039/TASK-207 acima): cada item ocupa só a largura da sua
     própria célula do grid, nunca a largura de conteúdo não quebrado. */
  .switcher{
    display:grid; grid-template-columns:1fr auto; align-items:center; gap:10px;
  }
  .switcher-left{ display:contents; }
  .switcher-left label{ grid-column:1 / -1; }
  .switcher-left select{ grid-column:1 / -1; width:100%; min-width:0; }
  #new-character-btn{ grid-column:1; justify-self:start; }
  .switcher-right{ grid-column:2; justify-self:end; }
}

/* ---------- Ability score plates ---------- */
.abilities{ display:grid; grid-template-columns:repeat(6,1fr); gap:12px; }
@media (max-width:760px){ .abilities{ grid-template-columns:repeat(3,1fr); } }
@media (max-width:420px){ .abilities{ grid-template-columns:repeat(2,1fr); } }
.plate{
  border:1px solid var(--iron); background:var(--panel-2);
  border-radius:6px; text-align:center; padding:30px 6px 12px; position:relative;
}
.plate .abbr{
  font-family:'Cinzel', serif; font-weight:700; font-size:11px;
  letter-spacing:0.16em; color:var(--ash-dim); text-transform:uppercase;
}
.plate .mod{
  font-family:'JetBrains Mono', monospace; font-weight:700;
  font-size:28px; color:var(--ember); margin:6px 0 2px; line-height:1;
}
.plate .score{
  font-family:'JetBrains Mono', monospace; font-size:12px; color:var(--ash);
  background:var(--ink); border:1px solid var(--iron-soft); border-radius:3px;
  display:inline-block; padding:1px 8px; margin-top:4px;
}

/* ---------- Saves & skills list ---------- */
.list-row{
  display:flex; align-items:center; gap:8px;
  padding:7px 4px; border-bottom:1px dashed var(--iron-soft); font-size:14.5px;
}
.list-row:last-child{ border-bottom:none; }
.list-row .name{ display:flex; align-items:center; gap:9px; }
.dot{ display:inline-block; width:9px; height:9px; border-radius:50%; border:1.5px solid var(--ash-dim); flex-shrink:0; }
.dot.filled{ background:var(--blood-bright); border-color:var(--blood-bright); }
.list-row .tag{ color:var(--ash-dim); font-size:12px; }
.list-row .val{ font-family:'JetBrains Mono', monospace; font-weight:700; color:var(--ember); min-width:32px; text-align:right; }

/* TASK-307 (ADR-076 § Decisão item 1, UC-010/UC-011): `.resource-tracker-row` (recursos
   limitados por classe) e `.spell-slot-tracker-row` (espaços de magia/Pact Magic) têm um
   terceiro bloco (`.resource-actions`) que `.list-row` genérico (saves/skills, sem esse
   bloco) nunca teve -- por isso a variante ganha `flex-wrap` própria, não o `.list-row` base. */
.resource-tracker-row, .spell-slot-tracker-row{ flex-wrap:wrap; row-gap:8px; }

.resource-actions{ display:flex; flex-wrap:wrap; gap:6px; margin-left:auto; }

/* Família visual de .add-btn, mas NÃO é .add-btn: width:100% ali existe para botões de largura
   total em listas verticais (ex. "+ Adicionar") -- aqui os botões vivem lado a lado numa linha
   horizontal. white-space:nowrap garante que o texto nunca quebra dentro do próprio botão; quando
   o espaço não permite os dois lado a lado, é a LINHA (.resource-actions, via flex-wrap do pai)
   que quebra para uma segunda linha, nunca o texto do botão. */
.resource-action-btn{
  display:inline-flex; align-items:center; gap:6px;
  background:transparent; border:1px dashed var(--iron); color:var(--ash);
  border-radius:5px; padding:8px 14px; font-family:'Cinzel', serif; font-size:12.5px;
  font-weight:600; letter-spacing:0.05em; cursor:pointer;
  white-space:nowrap; flex:0 0 auto;
  transition:all .15s ease;
}
.resource-action-btn:hover{ border-color:var(--ember); color:var(--ember); }
.resource-action-btn:disabled{ opacity:0.45; cursor:not-allowed; }

@media (max-width:480px){
  /* Mesmo breakpoint já usado pelo segundo degrau de .wiz-abilities-grid/.wiz-review-abilities
     (ADR-042 § Decisão item 4/UC-040) -- abaixo dele, força a linha de ações para sua própria
     linha (nome+contagem numa linha, ações na linha de baixo), em vez de depender só do
     flex-wrap "orgânico" do item acima (que já cobre a maioria dos casos, mas este degrau
     explícito torna o comportamento determinístico e testável nas larguras de referência do
     projeto -- 320/375/414/480px). */
  .resource-tracker-row .resource-actions, .spell-slot-tracker-row .resource-actions{
    flex:1 1 100%; justify-content:flex-end; margin-left:0;
  }
}

/* ---------- Combat readouts ---------- */
.readouts{ display:grid; grid-template-columns:repeat(4,1fr); gap:12px; }
@media (max-width:640px){ .readouts{ grid-template-columns:repeat(2,1fr); } }
.readout{ border:1px solid var(--iron); background:var(--panel-2); border-radius:6px; text-align:center; padding:16px 8px; }
.readout .big{ font-family:'JetBrains Mono', monospace; font-weight:700; font-size:30px; color:var(--bone); }
.readout .lbl{ font-family:'Cinzel', serif; font-size:11px; letter-spacing:0.12em; text-transform:uppercase; color:var(--ash-dim); margin-top:4px; }
.readout.hp .big{ color:var(--blood-bright); }
.readout.accent .big{ color:var(--ember); }
.readout.temp{ border-color:var(--iron); position:relative; }
.readout.temp .big, .readout.temp .efield-num{ color:var(--ash); }
.readout.temp.has-temp{ border-color:var(--ember-dim); background:color-mix(in srgb, var(--ember) 8%, var(--panel-2)); }
.readout.temp.has-temp .big{ color:var(--ember); }
.readout .shield-icon{ font-size:15px; opacity:0.8; }

/* ---------- Attack table ---------- */
table.attacks{ width:100%; border-collapse:collapse; font-size:14.5px; }
table.attacks th{
  text-align:left; font-family:'Cinzel', serif; font-size:11px; letter-spacing:0.1em;
  text-transform:uppercase; color:var(--ash-dim); padding:6px 8px; border-bottom:1px solid var(--iron);
}
table.attacks td{ padding:9px 8px; border-bottom:1px solid var(--iron-soft); }
table.attacks tr:last-child td{ border-bottom:none; }
table.attacks td.name{ color:var(--bone); font-weight:600; }
table.attacks td.atk{ font-family:'JetBrains Mono', monospace; color:var(--ember); }
table.attacks td.dmg{ font-family:'JetBrains Mono', monospace; color:var(--blood-bright); }

/* ---------- Attack row (edit mode, populated) ---------- */
.attack-row{ display:flex; flex-wrap:wrap; align-items:center; gap:8px 14px; width:100%; }
.attack-row-name{
  flex:1 1 150px; min-width:0; font-weight:600; color:var(--bone);
  overflow-wrap:anywhere;
}
.attack-row-actions{ display:flex; gap:6px; align-items:center; margin-left:auto; flex:0 0 auto; }
@media (max-width:480px){
  .attack-row-actions{ flex:1 1 100%; justify-content:flex-end; margin-left:0; }
}

/* ---------- Personality quad ---------- */
.trait-card{ border:1px solid var(--iron); background:var(--panel-2); border-radius:6px; padding:16px; }
.trait-card h4{
  margin:0 0 8px; font-family:'Cinzel', serif; font-size:12px; letter-spacing:0.14em;
  text-transform:uppercase; color:var(--blood-bright);
}
.trait-card p{ margin:0; font-size:14.5px; line-height:1.55; color:var(--ash); font-style:italic; }

/* ---------- Equipment ---------- */
.eq-list{ list-style:none; margin:0; padding:0; }
.eq-list li{
  display:flex; justify-content:space-between; gap:10px;
  padding:8px 2px; border-bottom:1px dashed var(--iron-soft); font-size:14.5px;
}
.eq-list li:last-child{ border-bottom:none; }
.eq-qty{ font-family:'JetBrains Mono', monospace; color:var(--ash-dim); flex-shrink:0; }

/* TASK-313 (UC-061/ADR-077): linha compacta de item de equipamento em modo de edição --
   renderEquipment() (ramo editável) passa a usar as mesmas 2 colunas do modo travado
   (#equipment-col-1/#equipment-col-2), cada item com nome + ×qty + nota, acrescido de
   .eq-edit-btn/.remove-btn. flex-wrap + overflow-wrap:anywhere no nome, mesma defesa contra
   overflow horizontal já usada por .attack-row (ADR-077 § Decisão item 1). */
.eq-row-compact{
  display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px;
  padding:8px 2px; border-bottom:1px dashed var(--iron-soft); font-size:14.5px;
}
.eq-row-compact:last-child{ border-bottom:none; }
.eq-row-compact .name{ flex:1 1 120px; min-width:0; overflow-wrap:anywhere; }
.eq-row-compact .eq-row-actions{ display:flex; gap:6px; align-items:center; margin-left:auto; flex:0 0 auto; }

.currency-row{ display:flex; gap:10px; flex-wrap:wrap; margin-top:12px; }
.coin{
  border:1px solid var(--iron-soft); border-radius:4px; padding:6px 12px; font-size:13px;
  font-family:'JetBrains Mono', monospace; color:var(--ash);
}
.coin b{ color:var(--ember); }

/* ---------- Feature accordion ---------- */
.accordion{ border:1px solid var(--iron); border-radius:6px; overflow:hidden; background:var(--panel-2); }
.acc-item + .acc-item{ border-top:1px solid var(--iron-soft); }
.acc-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:14px 18px; cursor:pointer; user-select:none; }
.acc-head:hover{ background:color-mix(in srgb, var(--ember) 6%, transparent); }
.acc-head h3{ margin:0; font-family:'Cinzel', serif; font-size:14.5px; letter-spacing:0.04em; color:var(--bone); font-weight:600; }
/* TASK-246/247 (UC-054/ADR-058 § Decisão item 1a): wraps the title so the "share to room chat"
   icon button (`.room-chat-share-btn`, only present inside a room, `currentRoomId`) sits right
   next to it on the same line -- `flex:1`/`min-width:0` keep this wrapper the one item that
   shrinks/grows inside `.acc-head`, same as `<h3>` did on its own before this wrapper existed. */
.acc-head-title-row{ display:flex; align-items:center; gap:8px; min-width:0; flex:1; }
.acc-head-title-row h3{ min-width:0; }
.acc-head .plus{ font-family:'JetBrains Mono', monospace; color:var(--ember); font-size:18px; transition:transform .2s ease; flex-shrink:0; }
.acc-item.open .plus{ transform:rotate(45deg); }
.acc-body{ max-height:0; overflow:hidden; transition:max-height .28s ease; padding:0 18px; }
.acc-item.open .acc-body{ padding:0 18px 18px; }
.acc-body p{ margin:0 0 10px; font-size:14.5px; line-height:1.65; color:var(--ash); }
.acc-body p:last-child{ margin-bottom:0; }
.acc-body strong{ color:var(--bone); }

/* ---------- Tenets banner ---------- */
.tenets-plate{
  position:relative;
  background: radial-gradient(ellipse at 30% 20%, color-mix(in srgb, var(--blood) 16%, transparent), transparent 55%), var(--panel-2);
  border:1px solid var(--iron); border-radius:6px; padding:26px 24px; overflow:hidden;
}
.tenets-plate::after{ content:""; position:absolute; inset:0; pointer-events:none; box-shadow: inset 0 0 40px rgba(0,0,0,0.55); }
.tenet{ margin-bottom:18px; padding-bottom:18px; border-bottom:1px solid var(--iron-soft); }
.tenet:last-child{ margin-bottom:0; padding-bottom:0; border-bottom:none; }
.tenet .tname{
  font-family:'Cinzel Decorative', serif; font-weight:700; font-size:16px;
  color:var(--blood-bright); letter-spacing:0.03em;
  text-shadow: 0 0 14px color-mix(in srgb, var(--blood-bright) 35%, transparent); margin-bottom:6px; display:block;
}
.tenet p{ margin:0; font-size:14.5px; line-height:1.65; color:var(--ash); }

/* ---------- Spell table & cards ---------- */
.slot-row{ display:flex; gap:10px; flex-wrap:wrap; margin-bottom:18px; }
.slot-chip{
  border:1px solid var(--iron); border-radius:20px; padding:6px 16px;
  font-family:'JetBrains Mono', monospace; font-size:13px; color:var(--ash); background:var(--panel-2);
}
.slot-chip b{ color:var(--ember); }
.spell-level-label{
  font-family:'Cinzel', serif; font-weight:700; font-size:13px; letter-spacing:0.12em;
  text-transform:uppercase; color:var(--ember); margin:22px 0 10px;
  padding-bottom:6px; border-bottom:1px solid var(--iron-soft);
}
.spell-level-label:first-child{ margin-top:0; }
/* ---------- Spellbook vertical spacing, ADR-063/UC-056, "décima sexta frente" -- flex/gap no
   contêiner em vez de depender de .spell-level-label:first-child (que continua existindo, sem
   efeito prático dentro desta aba, sem risco de regressão para qualquer outro uso da classe). ---------- */
#spellbook-container{ display:flex; flex-direction:column; gap:20px; }
/* Wrapper vazio (classe elegível mas sem item, cenário ADR-062) não deve reservar gap. */
#spellbook-container > *:empty{ display:none; }
.spell-levels-wrap{ display:flex; flex-direction:column; gap:20px; }
.spell-card{ border:1px solid var(--iron-soft); border-left:3px solid transparent; border-radius:6px; margin-bottom:8px; background:var(--panel-2); overflow:hidden; transition:border-color .15s ease; }
.spell-card.prepared{ border-left-color:var(--ember); }
/* Destaque informativo (nunca bloqueante, ADR-025 § Decisão, item 4) de "fora da lista de
   elegíveis" aplicado ao resultado da busca geral filtrado in-place (TASK-126/UC-022/ADR-030) --
   substitui, para este caminho, o antigo `.spell-suggestion--ineligible` de uma sugestão solta,
   já que o card filtrado é o próprio resultado agora. */
.spell-card.search-ineligible .spell-name{ color:var(--blood-bright); }
.spell-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:12px 16px; cursor:pointer; }
.spell-head:hover{ background:color-mix(in srgb, var(--ember) 6%, transparent); }
.spell-name-row{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.spell-name{ font-family:'Cinzel', serif; font-weight:600; font-size:14.5px; color:var(--bone); }
/* Reaproveitada por `room-sheet.js::spellMetaHtml` para a exibição de escola+nível de um bloco
   publicado no chat da sala -- não remover sem migrar aquele arquivo também (escopo diferente,
   fora de ADR-071/TASK-297, ver comentário lá). Deixou de ser usada por `sheet.js::buildSpellCard`
   (TASK-297/ADR-071 item 1): `school` migrou para dentro de `.spell-facts`, `level` virou
   `.spell-level-tag` abaixo. */
.spell-meta{ font-family:'JetBrains Mono', monospace; font-size:11.5px; color:var(--ash-dim); margin-top:2px; }
/* TASK-297 (ADR-071 item 1): nível da magia, ao lado do nome (cards sem nível -- cantrips,
   `sp.level === 0` -- simplesmente omitem esta tag, ver `buildSpellCard`). */
.spell-level-tag{ font-family:'JetBrains Mono', monospace; font-size:11px; color:var(--ash-dim); }
.spell-head-right{ display:flex; align-items:center; gap:14px; flex-shrink:0; }
.spell-check{ display:inline-flex; align-items:center; cursor:pointer; }
.spell-check input[type="checkbox"]{ width:17px; height:17px; accent-color:var(--ember); cursor:pointer; }
.spell-check.disabled{ opacity:0.4; cursor:not-allowed; }
.spell-check.disabled input[type="checkbox"]{ cursor:not-allowed; }
.spell-head .plus{ font-family:'JetBrains Mono', monospace; color:var(--ember); font-size:17px; flex-shrink:0; transition:transform .2s ease;}
.spell-card.open .plus{ transform:rotate(45deg); }
.spell-body{ max-height:0; overflow:hidden; transition:max-height .3s ease; padding:0 16px; }
.spell-card.open .spell-body{ padding:0 16px 16px; }
/* TASK-297 (ADR-071 item 1): `auto-fit`/`minmax` em vez de um `repeat(4,1fr)` fixo -- passou a
   acomodar 5 itens (escola entrou na grade) sem exigir uma segunda mudança manual se um fato a
   mais entrar no futuro; 100px é largura suficiente pro rótulo mais longo hoje ("Casting Time"/
   "Tempo de Conjuração") sem quebrar linha feio. */
.spell-facts{ display:grid; grid-template-columns:repeat(auto-fit, minmax(100px, 1fr)); gap:8px; margin-bottom:10px; font-size:12.5px; }
@media (max-width:640px){ .spell-facts{ grid-template-columns:repeat(2,1fr); } }
.spell-facts div{ background:var(--ink); border:1px solid var(--iron-soft); border-radius:4px; padding:6px 8px; min-width:0; }
.spell-facts .k{ color:var(--ash-dim); font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:0.08em; text-transform:uppercase; display:block; margin-bottom:2px;}
.spell-facts .v{ color:var(--bone); overflow-wrap:anywhere; word-break:break-word; }
.spell-desc{ font-size:14px; line-height:1.65; color:var(--ash); }
.spell-desc .higher{ margin-top:10px; padding-top:10px; border-top:1px dashed var(--iron-soft); color:var(--ember); font-size:13px; }

/* TASK-305 (UC-060/ADR-075): notação de dado clicável dentro da descrição de magia/cantrip
   (`linkifyDiceNotation`, sheet.js::buildSpellCard) -- estilo mínimo de link inline (herda a
   fonte/tamanho ao redor, sem padding/borda, para não quebrar o fluxo do parágrafo), sublinhado
   tracejado + cor ember (mesma paleta de `.higher` acima) para ficar visualmente distinguível como
   clicável dentro de `.spell-desc`/`.higher`. */
.dice-roll-link{
  display:inline; background:none; border:none; padding:0; margin:0; font:inherit;
  color:var(--ember-dim); text-decoration:underline dashed; cursor:pointer;
}
.dice-roll-link:hover{ color:var(--ember); }

.empty-note{ color:var(--ash-dim); font-size:14px; font-style:italic; }

/* ---------- Prepared spells counter ---------- */
.prep-counter{
  font-family:'JetBrains Mono', monospace; font-size:13px; color:var(--ash);
  padding-bottom:12px; border-bottom:1px solid var(--iron-soft);
}
.prep-counter b{ color:var(--ember); }
.prep-counter.over b{ color:var(--blood-bright); }

/* ---------- Spell search (autocomplete), TASK-109/UC-017/ADR-025 -- `.spell-search-results`/
   `.spell-suggestion` seguem em uso só pela busca de Oath Spells depois de TASK-126/UC-022/
   ADR-030; a busca geral do spellbook (`renderSpellbook`) passou a filtrar in-place as próprias
   seções de nível (`.spell-card.search-ineligible` acima é o destaque equivalente nesse
   caminho). ---------- */
.spell-search{ margin:4px 0 20px; }
/* TASK-267 correção pós-review (ADR-063 § Adendo): `.spell-search` é reaproveitada por
   `buildSpellSearchInput`, usada tanto por `renderSpellbook` (dentro de
   `#spellbook-container`, onde o espaçamento agora vem do `gap` do flex container acima) quanto
   por `renderOathSpells` (`#oath-spell-chips`, dentro de `#oath-spells-block`, um bloco IRMÃO
   de `#spellbook-container`, fora dele). A regra global `margin:0` da ADR-063 original removia
   a margem também do campo de Oath Spells, que não está dentro do flex/gap container e por isso
   ficava colado no texto/chips vizinhos. Regra base restaurada para a margem original (aplica a
   Oath Spells); `#spellbook-container .spell-search` abaixo zera especificamente a instância de
   dentro do Spellbook, onde o `gap` do contêiner já cobre o espaçamento (seletor descendente, não
   só `>`, para não depender de `.spell-search` continuar sendo filho direto do contêiner). */
#spellbook-container .spell-search{ margin:0; }
.spell-search-results{ display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.spell-suggestion{
  display:inline-flex; align-items:center; gap:6px;
  font-family:'JetBrains Mono', monospace; font-size:12.5px; color:var(--bone);
  background:var(--panel-2); border:1px solid var(--iron-soft); border-radius:4px;
  padding:6px 10px; transition:border-color .15s ease;
}
.spell-suggestion:hover{ border-color:var(--ember); }
/* TASK-123 (UC-021/ADR-029): a sugestão deixou de ser um único <button> clicável (agora é
   `.spell-suggestion-name`, um <button> sem moldura própria dentro do contêiner) para comportar
   o ícone "i" aditivo ao lado -- ver `buildSpellSearchInput` em sheet.js. */
.spell-suggestion-name{
  background:none; border:none; padding:0; margin:0; font:inherit; color:inherit; cursor:pointer;
}
/* Destaque informativo (nunca bloqueante, ADR-025 § Decisão, item 4) de "fora da lista de
   elegíveis"/"fora do esperado" -- hoje só se aplica às sugestões/chips de Oath Spells
   (`renderOathSpells`); a busca geral do spellbook usa `.spell-card.search-ineligible` desde
   TASK-126 (não existe mais como sugestão solta nesse caminho). */
.spell-suggestion--ineligible{ border-color:var(--blood-bright); color:var(--blood-bright); }
/* TASK-296 (ADR-070): `sheet.js::renderOathSpells` deixou de usar `.oath-spell-chip` (migrou para
   `.spell-card` via `buildSpellCard`) -- a regra abaixo continua aqui só porque `share.js` (a
   ficha pública/somente-leitura, `chipListHtml`, escopo diferente/fora desta tarefa) ainda
   renderiza Oath Spells como este chip; não remover sem migrar também `share.js`. */
.oath-spell-chip{ display:inline-flex; align-items:center; gap:6px; border:1px solid var(--iron-soft); border-radius:4px; padding:6px 10px; }

/* ---------- Tooltip component (TASK-123, UC-021/ADR-029) ----------
   Ícone "i" (`mode: 'click'`, único modo implementado nesta rodada) -- popover posicionado
   `position:absolute` relativo ao próprio gatilho, CSS puro (sem biblioteca de posicionamento,
   ADR-029 § Decisão, item 1/§ Alternativas consideradas). Funciona igual em mouse e touch porque
   é acionado por `click` (nunca `:hover`) -- ver `tooltip.js`. */
.tooltip-trigger{ position:relative; display:inline-flex; align-items:center; }
.info-icon{
  width:18px; height:18px; padding:0; border-radius:50%; border:1px solid var(--iron-soft);
  background:var(--panel-2); color:var(--ash-dim); font-family:'JetBrains Mono', monospace;
  font-size:11px; font-style:italic; font-weight:700; line-height:1; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; flex-shrink:0;
  transition:border-color .15s ease, color .15s ease;
}
.info-icon:hover{ border-color:var(--ember); color:var(--ember); }
.tooltip-popover{
  display:none; position:absolute; z-index:20; top:calc(100% + 6px); left:0;
  min-width:220px; max-width:min(320px, 80vw); background:var(--panel-2);
  border:1px solid var(--iron-soft); border-radius:6px; padding:10px 12px;
  box-shadow:0 6px 18px rgba(0,0,0,.35); font-size:13px; line-height:1.55; color:var(--ash);
}
.tooltip-popover.open{ display:block; }
.tooltip-field{ margin-bottom:6px; }
.tooltip-field:last-child{ margin-bottom:0; }
.tooltip-field .k{
  color:var(--ash-dim); font-family:'JetBrains Mono', monospace; font-size:10px;
  letter-spacing:0.08em; text-transform:uppercase; display:block; margin-bottom:2px;
}
.tooltip-field .v{ color:var(--bone); }

/* Painel de raça/classe do wizard de criação (TASK-124, UC-021/ADR-029 § Decisão, item 2) --
   NÃO é o `mode: 'live'` cogitado por `tooltip.js`/ADR-029 (nenhum gatilho clicável, nenhum
   popover posicionado): é um bloco simples, sempre embutido no fluxo normal do formulário,
   escondido via `display:none` quando nada está selecionado. Reaproveita `.tooltip-field`/`.k`/
   `.v` acima para o conteúdo (mesmo `renderTooltipBody`, ADR-029 § Decisão, item 1 -- "mesmo
   componente de renderização de conteúdo nos dois modos") -- só a moldura muda. */
.select-tooltip{
  display:none; margin-top:8px; background:var(--panel-2); border:1px solid var(--iron-soft);
  border-radius:6px; padding:10px 12px; font-size:13px; line-height:1.55; color:var(--ash);
}
.select-tooltip.open{ display:block; }

footer.foot{
  text-align:center; margin-top:34px; font-family:'JetBrains Mono', monospace;
  font-size:11px; letter-spacing:0.14em; text-transform:uppercase; color:var(--ash-dim);
}

/* ==========================================================================
   Global nav (TASK-118, UC-019, ADR-027)
   Barra fixa no topo -- reposiciona os controles de rolagem/histórico de
   dados (antes soltos em `.dice-widget`, `position:fixed; right/bottom`,
   causa raiz da colisão documentada aqui até TASK-102/TASK-118) e adiciona
   o menu do sistema (email, sair, Meu Conteúdo, Novo Personagem). A colisão
   que motivava `padding-right:220px` em `.tabpanel` deixou de existir: os
   dois controles que causavam a sobreposição agora vivem dentro desta barra
   fixa, uma região que nenhum conteúdo de aba ocupa -- sem substituto, ver
   ADR-027 § Decisão item 3. Mobile-first: só ícones, sem rótulo de texto
   (ADR-027 § Decisão item 5) -- `@media (min-width:761px)` abaixo só
   refina visualmente o drawer do menu (ancorado à direita em vez de tela
   cheia), nunca adiciona funcionalidade nova. */
.global-nav{
  /* TASK-232 (achado do code-reviewer, UC-053): z-index elevado de 500 para 1600 --
     `.wizard-overlay` (z-index:1500, compartilhado por #custom-content-screen/
     #rooms-screen/#preferences-screen/#wizard-screen/#levelup-screen) cobria fisicamente
     `#global-menu-btn` sempre que qualquer um desses overlays estava aberto, tornando
     "Acionar My Characters fecha qualquer overlay aberto" inatingível pela UI real. Novo
     valor fica acima de `.wizard-overlay` (1500) e abaixo de `.dice-overlay` (2000),
     `.situational-modal` (2500), `.loader-overlay` (3000) e `.toast` (4000) -- esses
     últimos três continuam por cima do menu global mesmo se aberto simultaneamente. */
  position:sticky; top:0; z-index:1600;
  display:flex; align-items:center; justify-content:flex-end; gap:10px;
  height:56px; padding:0 14px; box-sizing:border-box;
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border-bottom:1px solid var(--iron);
  box-shadow:var(--shadow);
}
.global-nav-btn{
  position:relative; width:40px; height:40px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  background:linear-gradient(135deg, var(--panel-2), var(--panel));
  border:1px solid var(--iron); cursor:pointer; box-shadow:var(--shadow);
  color:var(--ember); transition:transform .15s ease, border-color .15s ease;
}
.global-nav-btn:hover{ transform:translateY(-2px); border-color:var(--ember-dim); }
.global-nav-btn svg{ width:20px; height:20px; display:block; }

.global-menu-drawer{
  /* TASK-232: z-index elevado de 600 para 1700 (mesmo motivo de `.global-nav` acima) --
     mantém o drawer acima de `.global-nav` (1600) e de `.wizard-overlay` (1500), abaixo de
     `.dice-overlay`/`.situational-modal`/`.loader-overlay`/`.toast`. */
  position:fixed; top:56px; left:0; right:0; bottom:0; z-index:1700;
  display:flex; flex-direction:column; gap:10px; padding:20px;
  overflow-y:auto;
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border-top:1px solid var(--iron);
  box-shadow:var(--shadow);
}
.global-menu-header{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding-bottom:14px; margin-bottom:4px; border-bottom:1px solid var(--iron-soft);
}
.account-email{
  font-family:'JetBrains Mono', monospace; font-size:12px; color:var(--ash);
  overflow-wrap:anywhere;
}
.global-menu-close-btn{
  background:none; border:none; color:var(--ash-dim); font-size:22px; line-height:1;
  cursor:pointer; padding:0 2px; flex-shrink:0; transition:color .15s ease;
}
.global-menu-close-btn:hover{ color:var(--bone); }
.global-menu-item{
  display:flex; align-items:center; gap:8px; width:100%;
  background:var(--panel); color:var(--bone); border:1px solid var(--iron);
  border-radius:5px; padding:12px 14px; font-family:'Cinzel', serif; font-weight:600;
  font-size:13px; letter-spacing:0.03em; cursor:pointer; text-align:left;
  transition:all .15s ease;
}
.global-menu-item:hover{ border-color:var(--ember-dim); color:var(--ember); }
.global-menu-item-danger:hover{ border-color:var(--blood-bright); color:var(--blood-bright); }

@media (min-width:761px){
  .global-menu-drawer{
    left:auto; width:320px; border-left:1px solid var(--iron); border-top:none;
  }
}

/* ==========================================================================
   Dice roller
   ========================================================================== */
.dice-fab-icon{ width:24px; height:24px; display:block; flex-shrink:0; }
.dice-fab-icon svg{ width:100%; height:100%; display:block; }

.dice-history-badge{
  position:absolute; top:-3px; right:-3px; min-width:18px; height:18px; padding:0 4px;
  border-radius:9px; background:var(--blood-bright); color:#fff5ee;
  font-family:'JetBrains Mono', monospace; font-size:10.5px; font-weight:700;
  display:flex; align-items:center; justify-content:center; box-shadow:0 0 0 2px var(--ink);
}
.dice-history-badge.zero{ display:none; }

/* Ancorado abaixo da barra fixa (antes era filho flex de `.dice-widget`, sem
   posicionamento próprio -- agora precisa do seu próprio `position:fixed`,
   já que `.dice-widget` deixou de existir, TASK-118/ADR-027).

   Convenção "painel sobreposto responsivo" (ADR-042 § Decisão item 2, TASK-206/UC-041)
   -- aplicada aqui e a ser REAPLICADA (não reinventada) a qualquer seletor futuro que
   substitua este painel (ex. `#room-chat-panel` quando UC-037/TASK-176 remover este
   componente):
     - `position:fixed`, nunca `static`/`relative` -- sobrepõe o conteúdo por trás em vez
       de empurrá-lo (evita reflow horizontal do restante da página ao abrir/fechar).
     - largura: `min(<largura-alvo>, calc(100vw - <margem>))` (abaixo, no `@media`).
     - altura: `max-height:min(<altura-alvo>, calc(100vh - <offset-do-nav-fixo>))` --
       76px = altura de `.global-nav` (56px) + margem, mesmo cálculo usado por `top:64px`.
     - visibilidade via classe de toggle (`.show`), nunca `display:none` direto no elemento
       que anima -- preserva a transição de `opacity`/`transform` abaixo.
     - overflow vertical interno (`overflow-y:auto`) quando o conteúdo excede a altura
       calculada -- ver `.dice-history-list` mais abaixo, não neste seletor (o próprio
       painel usa `overflow:hidden` para não vazar a borda arredondada; quem rola é a
       lista interna). */
.dice-history-panel{
  position:fixed; top:64px; right:12px; z-index:500;
  width:300px; max-height:min(460px, calc(100vh - 76px));
  display:flex; flex-direction:column;
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border:1px solid var(--iron); border-radius:10px; box-shadow:var(--shadow); overflow:hidden;
  transform-origin:top right; transform:scale(0.92) translateY(-8px); opacity:0;
  pointer-events:none; transition:opacity .18s ease, transform .18s ease;
  container-type:inline-size;
}
.dice-history-panel.show{ opacity:1; transform:scale(1) translateY(0); pointer-events:auto; }
@media (max-width:560px){
  .dice-history-panel{ width:min(300px, calc(100vw - 52px)); right:8px; }
}
/* Container query escopada ao painel de chat (não substitui a @media max-width:640px de viewport
   usada pela ficha em .spell-facts, linha ~661) -- reduz para 2 colunas quando o contêiner real
   (não o viewport) é estreito. ADR-060 § Decisão item 6. */
@container (max-width:340px){
  .spell-facts{ grid-template-columns:repeat(2,1fr); }
}
.dice-history-header{
  display:flex; align-items:center; justify-content:space-between; padding:12px 14px;
  border-bottom:1px solid var(--iron-soft); font-family:'Cinzel', serif; font-weight:700;
  font-size:12.5px; letter-spacing:0.1em; text-transform:uppercase; color:var(--ember);
  flex-shrink:0;
}
.dice-history-actions{ display:flex; align-items:center; gap:8px; }
.dice-history-clear-btn{
  background:none; border:1px solid var(--iron-soft); border-radius:5px; padding:4px 10px;
  font-family:'JetBrains Mono', monospace; font-size:11px; color:var(--ash); cursor:pointer;
  text-transform:none; letter-spacing:0; transition:all .15s ease;
}
.dice-history-clear-btn:hover{ border-color:var(--blood-bright); color:var(--blood-bright); }
.dice-history-close-btn{
  background:none; border:none; color:var(--ash-dim); font-size:19px; line-height:1;
  cursor:pointer; padding:0 2px; transition:color .15s ease;
}
.dice-history-close-btn:hover{ color:var(--bone); }
.dice-history-list{ overflow-y:auto; padding:8px; display:flex; flex-direction:column; gap:6px; flex:1; min-height:120px; }
.dice-history-row{
  display:flex; align-items:center; gap:10px; padding:7px 9px; border-radius:6px;
  background:var(--ink); border:1px solid var(--iron-soft);
}
.dice-history-die{
  width:30px; height:30px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  font-family:'JetBrains Mono', monospace; font-weight:700; font-size:13px;
  background:var(--panel-2); border:1px solid var(--iron-soft); color:var(--ember);
}
.dice-history-die.crit{ background:color-mix(in srgb, #ffd76a 22%, var(--panel-2)); color:#ffd76a; border-color:#ffd76a; }
.dice-history-die.fail{ background:color-mix(in srgb, var(--blood-bright) 20%, var(--panel-2)); color:var(--blood-bright); border-color:var(--blood-bright); }
.dice-history-info{ flex:1; min-width:0; display:flex; flex-direction:column; }
.dice-history-label{ font-family:'Cinzel', serif; font-size:12.5px; color:var(--bone); font-weight:600; }
.dice-history-label .roller{ color:var(--ember); }
.dice-history-time{
  font-family:'JetBrains Mono', monospace; font-size:10.5px; color:var(--ash-dim);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer;
}
.dice-history-time.expanded{ white-space:normal; overflow:visible; text-overflow:clip; word-break:break-word; }
.dice-history-empty{ text-align:center; padding:32px 14px; color:var(--ash-dim); font-size:13px; font-style:italic; }

/* Chat-style message entries (plain text "said" by the current character) */
.dice-history-message{
  padding:8px 10px; border-radius:6px; background:color-mix(in srgb, var(--ember) 7%, var(--ink));
  border:1px solid var(--iron-soft); border-left:3px solid var(--ember-dim);
}
.dice-history-message.error{ border-left-color:var(--blood-bright); }
.dice-history-message-top{ display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.dice-history-message-char{
  font-family:'Cinzel', serif; font-weight:700; font-size:12px; color:var(--ember);
  letter-spacing:0.03em;
}
.dice-history-message-time{ font-family:'JetBrains Mono', monospace; font-size:10px; color:var(--ash-dim); flex-shrink:0; }
.dice-history-message-text{
  font-family:'Crimson Pro', serif; font-style:italic; font-size:13.5px; color:var(--ash);
  margin-top:2px; line-height:1.4; word-break:break-word;
}

/* TASK-247 (UC-054/ADR-058 § Decisão item 7): chat-style entries for a shared content block
   (`kind: 'content'`) -- third variant of the room log, visually distinct from both a free-text
   message (`.dice-history-message` above, `--ember-dim` left border) and a roll
   (`.dice-history-row`, no left accent at all) via `--blood-bright` on the left border, reused
   from the same palette already used for crit/fail dice accents rather than a brand-new token.
   `.dice-history-message-char`/`.dice-history-message-time` (above) are reused as-is inside
   `.dice-history-content-top` -- only the badge in between and the outer card/name/body are new. */
.dice-history-content{
  padding:8px 10px; border-radius:6px;
  background:color-mix(in srgb, var(--blood-bright) 6%, var(--ink));
  border:1px solid var(--iron-soft); border-left:3px solid var(--blood-bright);
}
.dice-history-content-top{ display:flex; align-items:center; flex-wrap:wrap; gap:8px; }
.dice-history-content-top .dice-history-message-time{ margin-left:auto; }
.dice-history-content-badge{
  font-family:'JetBrains Mono', monospace; font-weight:700; font-size:10px;
  letter-spacing:0.06em; text-transform:uppercase; color:var(--blood-bright);
  background:color-mix(in srgb, var(--blood-bright) 14%, var(--ink));
  border:1px solid var(--blood-bright); border-radius:9px; padding:2px 8px; flex-shrink:0;
}
.dice-history-content-name{
  font-family:'Cinzel', serif; font-weight:700; font-size:14px; color:var(--bone); margin-top:6px;
}
.dice-history-content-body{
  font-family:'Crimson Pro', serif; font-size:13.5px; color:var(--ash);
  margin-top:6px; line-height:1.55; word-break:break-word; white-space:pre-wrap;
}
/* "At Higher Levels" -- same rendering already used by `.spell-desc .higher` (buildSpellCard,
   sheet.js) below, but `contentRowHtml` (room-sheet.js) renders `<div class="higher">` as a
   direct sibling here, never wrapped in `.spell-desc` -- own rule, same declarations. */
.dice-history-content .higher{
  margin-top:10px; padding-top:10px; border-top:1px dashed var(--iron-soft);
  color:var(--ember); font-size:13px;
}

/* Chat-style input pinned to the bottom of the panel */
.dice-chat-form{
  display:flex; align-items:center; gap:8px; padding:10px; flex-shrink:0;
  border-top:1px solid var(--iron-soft); background:var(--panel-2);
}
.dice-chat-input{
  flex:1; min-width:0; background:var(--ink); border:1px solid var(--iron-soft); border-radius:20px;
  padding:8px 14px; font-family:'Crimson Pro', serif; font-size:13.5px; color:var(--bone);
}
.dice-chat-input::placeholder{ color:var(--ash-dim); }
.dice-chat-input:focus{ outline:none; border-color:var(--ember-dim); }
.dice-chat-send{
  flex-shrink:0; width:32px; height:32px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:var(--ember); border:none; color:var(--on-accent); cursor:pointer; transition:filter .15s ease;
}
.dice-chat-send:hover{ filter:brightness(1.1); }
.dice-chat-send svg{ width:15px; height:15px; }

.dice-overlay{
  position:fixed; inset:0; z-index:2000;
  display:flex; align-items:center; justify-content:center;
  background:rgba(6,4,2,0.62); backdrop-filter:blur(3px);
  opacity:0; pointer-events:none; transition:opacity .25s ease;
  padding:20px;
}
.dice-overlay.show{ opacity:1; pointer-events:auto; }
.dice-overlay-inner{ display:flex; flex-direction:column; align-items:center; gap:18px; max-width:100%; }

/* Wrapping row so multiple dice sit side by side and drop to a new line
   when there isn't enough width for all of them. */
.dice-row{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:14px; max-width:min(90vw, 680px);
}

.dice-stage{ position:relative; flex-shrink:0; }
.dice-stage.size-lg{ width:240px; height:240px; }
.dice-stage.size-sm{ width:140px; height:140px; }

/* Situational/bonus dice (e.g. a confirmed "+1d8 vs. undead" clause) get a
   violet glow so they read as distinct from the standard damage dice sitting
   right next to them — deliberately a different color from the gold/red
   crit-fail glow so the two meanings never get confused. */
.dice-stage.situational .d20-shape svg{
  filter: drop-shadow(0 0 5px #b98aff) drop-shadow(0 0 14px rgba(185,138,255,0.75)) drop-shadow(0 16px 20px rgba(0,0,0,0.5));
}
.dice-stage.situational::after{
  content:'BONUS';
  position:absolute; left:50%; bottom:-6px; transform:translateX(-50%);
  font-family:'JetBrains Mono', monospace; font-size:9px; font-weight:700; letter-spacing:0.12em;
  color:#c9a7ff; background:rgba(24,14,36,0.85); border:1px solid rgba(185,138,255,0.5);
  border-radius:8px; padding:2px 7px; white-space:nowrap; pointer-events:none;
}

.dice-shadow{
  position:absolute; left:50%; bottom:2px; width:130px; height:24px; margin-left:-65px;
  background:radial-gradient(ellipse at center, rgba(0,0,0,0.6), transparent 72%);
  border-radius:50%; filter:blur(2px);
}
.dice-stage.size-sm .dice-shadow{ width:76px; height:14px; margin-left:-38px; }

.d20-shape{ position:absolute; inset:0; }
.d20-shape svg{ width:100%; height:100%; filter:drop-shadow(0 16px 22px rgba(0,0,0,0.55)); display:block; }
.d20-shape.rolling{ animation: dice-throw 1s cubic-bezier(.22,.9,.24,1) forwards; }

@keyframes dice-throw{
  0%{ transform:translate(var(--start-x,0px), var(--start-y,0px)) scale(0.4) rotate(0deg); opacity:0; }
  10%{ opacity:1; }
  55%{ transform:translate(0,0) scale(1.16) rotate(calc(var(--spins, 720deg) * 0.7)); }
  78%{ transform:translate(0,0) scale(0.92) rotate(calc(var(--spins, 720deg) * 0.94)); }
  100%{ transform:translate(0,0) scale(1) rotate(var(--spins, 720deg)); opacity:1; }
}

/* Natural roll number, pinned to the exact center of each die with a dark
   halo behind it so it stays legible over any combination of facet colors. */
.dice-result-natural{
  position:absolute; inset:0; margin:auto; width:78px; height:78px;
  display:flex; align-items:center; justify-content:center; border-radius:50%;
  background:radial-gradient(circle, rgba(6,4,2,0.62) 0%, rgba(6,4,2,0.28) 65%, transparent 100%);
  font-family:'Cinzel Decorative', serif; font-weight:900; font-size:50px; color:#f3ead9;
  opacity:0; transform:scale(0.6); pointer-events:none;
  transition:opacity .25s ease, transform .25s ease;
  text-shadow:0 0 5px rgba(0,0,0,0.9), 0 2px 6px rgba(0,0,0,0.9);
}
.dice-stage.size-sm .dice-result-natural{ width:48px; height:48px; font-size:28px; }
.dice-result-natural.show{ opacity:1; transform:scale(1); }
.dice-result-natural.crit{
  color:#ffd76a;
  background:radial-gradient(circle, rgba(120,80,10,0.5) 0%, rgba(6,4,2,0.3) 65%, transparent 100%);
  text-shadow:0 0 16px rgba(255,183,66,0.9), 0 2px 6px rgba(0,0,0,0.9);
}
.dice-result-natural.fail{ color:#ff8a8a; }

/* Roll label + total, shown below the die in a solid, always-legible panel.
   Colors here are intentionally fixed (not theme variables) since this
   panel is always dark, independent of the active sheet theme. */
.dice-result-footer{
  display:flex; flex-direction:column; align-items:center; gap:5px;
  background:rgba(8,6,4,0.82); border:1px solid rgba(255,255,255,0.09);
  border-radius:10px; padding:11px 20px; min-width:180px; max-width:min(90vw, 420px);
  box-shadow:0 8px 20px rgba(0,0,0,0.4);
  opacity:0; transform:translateY(8px);
  transition:opacity .25s ease, transform .25s ease;
}
.dice-result-footer.show{ opacity:1; transform:translateY(0); }
.dice-result-footer.crit{ border-color:#ffd76a; box-shadow:0 0 20px rgba(255,183,66,0.35); }
.dice-result-footer.fail{ border-color:#e2555f; box-shadow:0 0 20px rgba(226,85,95,0.35); }
.dice-result-label{
  font-family:'Cinzel', serif; font-weight:700; font-size:13px; letter-spacing:0.07em;
  text-transform:uppercase; color:#e0a251; text-align:center; max-width:240px;
}
.dice-result-total{
  font-family:'JetBrains Mono', monospace; font-size:14px; color:#e9dfc7; text-align:center;
}

.dice-hint{
  text-align:center;
  font-family:'JetBrains Mono', monospace; font-size:11px; letter-spacing:0.1em;
  text-transform:uppercase; color:#cfc7b8;
  background:rgba(8,6,4,0.72); border:1px solid rgba(255,255,255,0.08);
  border-radius:20px; padding:6px 16px;
  opacity:0; transition:opacity .3s ease .25s;
}
.dice-overlay.show .dice-hint{ opacity:0.9; }

.dice-overlay-close{
  position:absolute; top:18px; right:18px; z-index:1;
  width:38px; height:38px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:rgba(8,6,4,0.72); border:1px solid rgba(255,255,255,0.12);
  color:#e9dfc7; font-size:22px; line-height:1; cursor:pointer;
  transition:all .15s ease;
}
.dice-overlay-close:hover{ background:rgba(8,6,4,0.9); border-color:rgba(255,255,255,0.25); transform:scale(1.06); }

.d20-svg .facet-light{ fill:var(--ember); stroke:color-mix(in srgb, var(--ink) 35%, var(--ember)); stroke-width:1; }
.d20-svg .facet-dark{ fill:var(--blood); stroke:color-mix(in srgb, var(--ink) 45%, var(--blood)); stroke-width:1; }
.d20-svg .d20-outline{ fill:none; stroke:var(--bone); stroke-width:2; opacity:0.55; }

/* ---------- App loader (TASK-218, UC-046, ADR-051) ----------
   Overlay reutilizável (showLoader()/hideLoader(), loader.js) -- classe nova,
   não uma extensão de .dice-overlay (ciclos de vida diferentes: .dice-overlay
   anima um arremesso pontual com resultado, .loader-overlay gira
   indefinidamente até hideLoader()). Reaproveita .d20-svg/.facet-light/
   .facet-dark/.d20-outline acima (já theming-aware), sem tocar nelas. */
.loader-overlay{
  position:fixed; inset:0; z-index:3000; /* acima de .situational-modal (2500), o mais alto do produto */
  display:flex; align-items:center; justify-content:center;
  background:rgba(6,4,2,0.62); backdrop-filter:blur(3px);
}
.loader-die{ width:96px; height:96px; animation:loader-spin 1.4s linear infinite; }
.loader-die svg{ width:100%; height:100%; display:block; filter:drop-shadow(0 10px 16px rgba(0,0,0,0.5)); }
@keyframes loader-spin{ to{ transform:rotate(360deg); } }

/* ---------- Toast (TASK-225, UC-048, ADR-053) ----------
   Feedback textual transitório (sucesso/erro), toast.js::showToast() -- substitui
   #save-status (removido de .edit-controls). position:fixed, fora do fluxo normal do
   documento, então uma mensagem longa nunca desalinha nenhum controle vizinho. Ancorado à
   parte inferior (bottom, não top) para não competir visualmente com header.global-nav
   fixo -- mesma composição própria de position:fixed + centralização já usada por outros
   overlays do produto (nenhum valor idêntico entre todos, ADR-053 § Decisão item 2). */
.toast{
  position:fixed; left:50%; bottom:24px; transform:translateX(-50%);
  z-index:4000; /* acima de .loader-overlay (3000), o mais alto do produto -- o toast pode
                   aparecer DURANTE um loader (ex. erro ao listar personagens logo após o
                   login) e precisa ficar visível por cima */
  max-width:min(90vw, 420px); padding:12px 18px;
  background:var(--panel); border:1px solid var(--iron); border-radius:6px; box-shadow:var(--shadow);
  color:var(--bone); font-size:14px; text-align:center;
}
.toast.error{ border-color:var(--blood-bright); color:var(--blood-bright); }

/* ---------- Situational bonus-dice modal ---------- */
.situational-modal{
  position:fixed; inset:0; z-index:2500;
  display:flex; align-items:center; justify-content:center;
  background:rgba(4,3,2,0.72); backdrop-filter:blur(3px);
  opacity:0; pointer-events:none; transition:opacity .2s ease;
  padding:20px;
}
.situational-modal.show{ opacity:1; pointer-events:auto; }
.situational-modal-box{
  width:min(92vw, 380px);
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border:1px solid var(--iron); border-radius:12px;
  padding:26px 24px 22px; text-align:center;
  box-shadow:0 20px 50px rgba(0,0,0,0.55);
  transform:scale(0.94) translateY(6px); transition:transform .2s ease;
}
.situational-modal.show .situational-modal-box{ transform:scale(1) translateY(0); }
.situational-modal-icon{
  width:46px; height:46px; margin:0 auto 14px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, #b98aff 18%, var(--panel-2));
  border:1px solid rgba(185,138,255,0.5); color:#c9a7ff; font-size:22px;
}
.situational-modal-title{
  font-family:'Cinzel', serif; font-weight:700; font-size:15px; letter-spacing:0.04em;
  color:var(--ember); margin-bottom:8px;
}
.situational-modal-desc{
  font-family:'Crimson Pro', serif; font-size:15px; line-height:1.5; color:var(--ash);
  margin-bottom:20px;
}
.situational-modal-actions{ display:flex; gap:10px; }
.situational-modal-btn{
  flex:1; padding:11px 10px; border-radius:8px; cursor:pointer;
  font-family:'Cinzel', serif; font-weight:600; font-size:13px; letter-spacing:0.04em;
  transition:all .15s ease;
}
.situational-modal-btn.decline{
  background:transparent; border:1px solid var(--iron-soft); color:var(--ash);
}
.situational-modal-btn.decline:hover{ border-color:var(--ash-dim); color:var(--bone); }
.situational-modal-btn.accept{
  background:linear-gradient(135deg, #c9a7ff, #9b6ee8); border:1px solid #b98aff; color:#1c1226;
}
.situational-modal-btn.accept:hover{ filter:brightness(1.08); }

/* ---------- Add/edit attack modal (TASK-304, UC-059/ADR-073) -- mesmo espírito de
   .situational-modal acima (backdrop fixo, caixa centralizada), largura maior para acomodar
   quatro campos empilhados verticalmente (.efield-group já é block-level, sem CSS extra
   necessário para o empilhamento em si) -- min(92vw, 420px) garante zero rolagem horizontal de
   320px a 1024px (mesmo critério de UC-039). TASK-313 (UC-061/ADR-077): `#equipment-modal`
   reaproveita esta MESMA classe/caixa literalmente (só o `id` muda) -- nenhum CSS de overlay
   novo, ver ADR-077 § Decisão item 2/Alternativas item 5. */
.attack-modal{
  position:fixed; inset:0; z-index:2500;
  display:flex; align-items:center; justify-content:center;
  background:rgba(4,3,2,0.72); backdrop-filter:blur(3px);
  opacity:0; pointer-events:none; transition:opacity .2s ease;
  padding:20px;
}
.attack-modal.show{ opacity:1; pointer-events:auto; }
.attack-modal-box{
  width:min(92vw, 420px);
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
  border:1px solid var(--iron); border-radius:12px;
  padding:22px 22px 20px;
  box-shadow:0 20px 50px rgba(0,0,0,0.55);
  transform:scale(0.94) translateY(6px); transition:transform .2s ease;
}
.attack-modal.show .attack-modal-box{ transform:scale(1) translateY(0); }
.attack-modal-title{
  font-family:'Cinzel', serif; font-weight:700; font-size:15px; letter-spacing:0.04em;
  color:var(--ember); margin-bottom:16px; text-align:center;
}
.attack-modal-box .efield-group{ margin-bottom:12px; }

/* Ícone de editar um ataque/item de equipamento existente (renderAttacks/renderEquipment, ramo
   editável) -- mesma família visual de .remove-btn/.room-chat-share-btn acima (borda fina, sem
   destaque até hover), cor de hover neutra (--ember-dim) por não ser uma ação destrutiva nem de
   publicação. TASK-313 (UC-061/ADR-077): `.eq-edit-btn` (equipment) reaproveita esta mesma folha
   de regra via seletor combinado -- `.attack-edit-btn` preservado intocado, ver ADR-077 § Decisão
   item 2. */
.attack-edit-btn, .eq-edit-btn{
  background:none; border:1px solid var(--iron-soft); color:var(--ash-dim);
  border-radius:4px; width:24px; height:24px; padding:0; flex-shrink:0; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; transition:all .15s ease;
}
.attack-edit-btn svg, .eq-edit-btn svg{ width:14px; height:14px; }
.attack-edit-btn:hover, .eq-edit-btn:hover{ border-color:var(--ember-dim); color:var(--ember); }

/* ---------- Inline roll-check icons ---------- */
.roll-icon-btn{
  display:inline-flex; align-items:center; justify-content:center; flex-shrink:0;
  width:22px; height:22px; border-radius:50%; padding:0;
  background:transparent; border:1px solid var(--iron-soft); color:var(--ember-dim);
  cursor:pointer; transition:all .15s ease;
}
.roll-icon-btn:hover{
  color:var(--ember); border-color:var(--ember-dim);
  background:color-mix(in srgb, var(--ember) 12%, transparent); transform:scale(1.1);
}
.roll-icon-btn:active{ transform:scale(0.95); }
.roll-icon-btn svg{ width:13px; height:13px; }

.row-main{ flex:1; min-width:0; display:flex; align-items:center; justify-content:space-between; gap:8px; }

.plate .roll-icon-btn{ position:absolute; top:8px; right:8px; }

.atk-cell{ display:flex; align-items:center; gap:8px; }

/* ==========================================================================
   Wizard de criação de personagem (TASK-013, UC-004)
   Overlay modal, mesmo espírito de .dice-overlay/.situational-modal.
   ========================================================================== */
/* ADR-076 § Decisão item 2: o overlay reserva a faixa da navbar diretamente
   no seu próprio `inset` -- a caixa do overlay simplesmente não existe na
   faixa 0--`--nav-height`, então nada dentro dela (nem o próprio backdrop
   escurecido) pode visualmente ocupar essa região. `z-index:1500` permanece
   INALTERADO (continua abaixo de `.global-nav`, 1600, TASK-232) -- a
   correção é geométrica, não uma inversão de pilha de empilhamento. */
.wizard-overlay{
  position:fixed; inset:0; top:var(--nav-height); z-index:1500;
  display:flex; align-items:center; justify-content:center;
  background:rgba(6,4,2,0.65); backdrop-filter:blur(3px);
  padding:20px;
}
/* `max-height` reduzido pela mesma `--nav-height`: sem isso, `align-items:
   center` do overlay deixaria um `.wizard-box` alto o bastante transbordar
   para cima/baixo dos limites do overlay (flexbox não recorta um item
   centralizado maior que o container), reabrindo o bug do TASK-232 por
   outro caminho -- ver ADR-076 § Decisão item 2. `overflow-y:auto`
   permanece inalterado (rolagem interna já cobre viewports curtos). */
.wizard-box{
  width:min(94vw, 720px); max-height:calc(88vh - var(--nav-height)); overflow-y:auto;
  display:flex; flex-direction:column; gap:16px;
}
/* TASK-058: sem `flex-shrink:0`, o algoritmo padrão de flexbox (todo item
   com `flex-shrink:1` por padrão) comprime `.wizard-header`/`.wizard-steps`/
   `.wizard-body`/`.wizard-errors`/`.wizard-warnings`/`.wizard-nav` para
   caber dentro de `max-height:88vh` do `.wizard-box` ANTES de deixar o
   próprio `.wizard-box` estourar e rolar (`overflow-y:auto`) -- mas o
   conteúdo real (grade de ASI, chips de magia, texto de erro) não encolhe de
   fato (não há wrap/redução de conteúdo possível), então ele transborda
   visualmente para fora da caixa já comprimida de `.wizard-body`,
   sobrepondo `.wizard-nav` (que é posicionado logo depois no fluxo,
   calculado a partir da altura comprimida, não da altura real do conteúdo
   transbordado). Resultado: os botões `[data-asi-inc]`/`[data-asi-dec]` da
   primeira linha da grade de ASI ficam fisicamente cobertos por
   "Voltar"/"Avançar" -- um clique real do usuário (ou do Puppeteer) na
   posição do botão de incremento na verdade atinge `.wizard-nav`, que está
   por cima na pilha de renderização, e não faz nada (achado confirmado por
   captura de tela em TASK-058, reproduzido de forma 100% determinística,
   independente de timing/race -- não um bug de teste). `flex-shrink:0` aqui
   garante que nenhum desses blocos seja comprimido abaixo do seu conteúdo
   real -- quando a soma das alturas reais excede `max-height:88vh`, é a
   PRÓPRIA `.wizard-box` que rola (comportamento já pretendido pelo
   `overflow-y:auto` existente nela), nunca sobreposição visual entre
   blocos. */
.wizard-header{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex-shrink:0; }
.wizard-close-btn{
  background:none; border:1px solid var(--iron-soft); color:var(--ash-dim);
  border-radius:50%; width:30px; height:30px; line-height:1; cursor:pointer;
  font-size:18px; flex-shrink:0; transition:all .15s ease;
}
.wizard-close-btn:hover{ border-color:var(--blood-bright); color:var(--blood-bright); }

.wizard-steps{
  display:flex; flex-wrap:wrap; gap:6px 14px;
  font-family:'JetBrains Mono', monospace; font-size:11px; letter-spacing:0.06em;
  text-transform:uppercase; color:var(--ash-dim);
  flex-shrink:0;
}
.wizard-step-item{ display:flex; align-items:center; gap:6px; }
.wizard-step-num{
  display:inline-flex; align-items:center; justify-content:center;
  width:18px; height:18px; border-radius:50%; border:1px solid var(--iron-soft);
  font-size:10px;
}
.wizard-step-item.active{ color:var(--ember); }
.wizard-step-item.active .wizard-step-num{ border-color:var(--ember); color:var(--ember); }
.wizard-step-item.done{ color:var(--ash); }
.wizard-step-item.done .wizard-step-num{ border-color:var(--ash-dim); background:var(--iron-soft); }

.wizard-body{ min-height:120px; flex-shrink:0; }
.wizard-errors{
  background:color-mix(in srgb, var(--blood-bright) 10%, var(--panel-2));
  border:1px solid var(--blood-bright); border-radius:6px; padding:10px 14px;
  color:var(--blood-bright); font-size:13.5px;
  flex-shrink:0;
}
.wizard-errors ul{ margin:0; padding-left:18px; }
.wizard-errors li{ margin:2px 0; }

/* TASK-015: bloco de avisos não bloqueantes (ADR-009/TASK-018 -- campo
   `warnings` aditivo, ainda não emitido por nenhuma versão atual do
   servidor). Paleta âmbar/ember, distinta de .wizard-errors (vermelho), para
   não ler como uma falha do level up. */
.wizard-warnings{
  background:color-mix(in srgb, var(--ember) 10%, var(--panel-2));
  border:1px solid var(--ember-dim); border-radius:6px; padding:10px 14px;
  color:var(--ember); font-size:13.5px;
  flex-shrink:0;
}
.wizard-warnings ul{ margin:4px 0 0; padding-left:18px; }
.wizard-warnings li{ margin:2px 0; }

.wizard-nav{ display:flex; justify-content:space-between; gap:10px; flex-shrink:0; }

/* TASK-015: botão "Level Up" -- mesmo tratamento visual de desabilitado que
   .icon-btn.save-btn:disabled já dá ao botão Save (ver "Edit controls"
   acima), aplicado aqui de forma escopada por id em vez de generalizar
   .icon-btn:disabled (fora do escopo desta tarefa mudar o comportamento de
   outros botões .icon-btn, ex. os de navegação dos wizards). */
#level-up-btn:disabled{ opacity:0.45; cursor:not-allowed; }

/* TASK-026: botão "Apagar Personagem" -- mesmo tratamento visual de
   desabilitado (escopado por id, mesma justificativa de #level-up-btn
   acima); hover em vermelho (--blood-bright, já usado para estados de
   erro/perigo em .save-status.error e #lock-toggle.unlocked) para sinalizar
   que é uma ação destrutiva antes mesmo do window.confirm aparecer. */
#delete-character-btn:disabled{ opacity:0.45; cursor:not-allowed; }
#delete-character-btn:not(:disabled):hover{ border-color:var(--blood-bright); color:var(--blood-bright); }

.wiz-points-remaining{
  font-family:'JetBrains Mono', monospace; font-size:14px; color:var(--ash);
  margin-bottom:14px;
}
.wiz-points-remaining b{ color:var(--ember); }
.wiz-points-remaining.wiz-points-invalid b{ color:var(--blood-bright); }

.wiz-abilities-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
@media (max-width:560px){ .wiz-abilities-grid{ grid-template-columns:repeat(2,1fr); } }
/* TASK-205 (UC-040/ADR-042 § Decisão item 1): 2 colunas ainda "vaza" em telas bem estreitas --
   cada `.wiz-ability-card` (usado pela grade de distribuição de ASI de `#levelup-screen` e pela
   grade de bônus de atributo por raça de `#custom-content-screen`) precisa de espaço para dois
   `.remove-btn` (24px cada) + `.wiz-ability-score` (ex. "16 → 18") lado a lado dentro de
   `.wiz-ability-controls`, sem quebra de linha própria -- em 2 colunas, isso já não cabe com
   folga abaixo de ~480px de viewport (largura útil de `.wizard-box` menor que a soma dos
   controles). Um segundo degrau para 1 coluna, no mesmo breakpoint usado pelos critérios de
   aceite deste UC (480px), dá a cada card a largura cheia da caixa. */
@media (max-width:480px){ .wiz-abilities-grid{ grid-template-columns:1fr; } }
.wiz-ability-card{
  border:1px solid var(--iron); background:var(--panel-2); border-radius:6px;
  text-align:center; padding:12px 6px;
}
.wiz-ability-label{
  font-family:'Cinzel', serif; font-weight:700; font-size:11px; letter-spacing:0.1em;
  text-transform:uppercase; color:var(--ash-dim); margin-bottom:8px;
}
.wiz-ability-controls{ display:flex; align-items:center; justify-content:center; gap:10px; }
.wiz-ability-score{
  font-family:'JetBrains Mono', monospace; font-weight:700; font-size:20px; color:var(--bone);
  min-width:28px;
}
.wiz-ability-mod{ font-family:'JetBrains Mono', monospace; font-size:12.5px; color:var(--ember); margin-top:6px; }

.wiz-skills-section + .wiz-skills-section, .wiz-spells-section + .wiz-spells-section{ margin-top:18px; }
.wiz-skills-note{ font-size:13.5px; color:var(--ash); margin-bottom:8px; }
.wiz-skill-chips{ display:flex; flex-wrap:wrap; gap:8px; }
.wiz-skill-chip{
  display:inline-flex; align-items:center; gap:6px;
  border:1px solid var(--iron-soft); border-radius:20px; padding:5px 12px;
  font-size:13px; color:var(--ash); background:var(--panel-2);
}
.wiz-skill-chip.granted{ border-color:var(--ember-dim); color:var(--ember); }

.wiz-skill-options, .wiz-spell-options{ display:flex; flex-wrap:wrap; gap:8px 16px; }
/* TASK-123 (UC-021/ADR-029): envolve `.wiz-skill-option` (um <label> de checkbox) + o ícone "i"
   como irmãos, nunca aninhado dentro do <label> -- caso contrário, clicar no ícone também
   marcaria/desmarcaria o checkbox associado (comportamento nativo de <label>). Ver
   `renderSpellsStep` em wizard-create.js. */
.wiz-skill-option-row{ display:inline-flex; align-items:center; gap:4px; }
.wiz-skill-option{
  display:inline-flex; align-items:center; gap:7px; font-size:14px; color:var(--bone); cursor:pointer;
}
.wiz-skill-option input{ accent-color:var(--ember); cursor:pointer; }
.wiz-skill-option.disabled{ opacity:0.4; cursor:not-allowed; }
.wiz-skill-option.disabled input{ cursor:not-allowed; }

.wiz-subclass-note{ font-size:14px; color:var(--ash); margin-bottom:14px; line-height:1.5; }
/* NOVO (TASK-141, UC-028): anotação de nível total, exibida só em contexto multiclasse dentro
   de uma nota já existente (wiz-subclass-note) -- levemente mais discreta que o texto ao redor. */
.wiz-multiclass-note{ font-size:12.5px; opacity:0.85; }
.wiz-subclass-options{ display:flex; flex-direction:column; gap:8px; }
.wiz-subclass-option{
  display:flex; align-items:center; gap:10px; border:1px solid var(--iron-soft);
  border-radius:6px; padding:10px 14px; cursor:pointer; font-size:14.5px; color:var(--bone);
  transition:all .15s ease;
}
.wiz-subclass-option input{ accent-color:var(--ember); cursor:pointer; }
.wiz-subclass-option.selected, .wiz-subclass-option:hover{ border-color:var(--ember-dim); }

/* TASK-205 (UC-040/ADR-042 § Decisão item 1): `.kv-row` sem `flex-wrap` forçava `.v` a
   permanecer na mesma linha de `.k` mesmo quando o valor era um texto longo (lista de magias/
   perícias, distribuição de ASI etc.) -- combinado com `flex-shrink:0` em `.k`, o rótulo também
   nunca cedia espaço, então a soma das duas larguras "vazava" para fora de `.wizard-box` em
   telas estreitas. `flex-wrap:wrap` permite que `.v` quebre para a linha seguinte quando não cabe
   ao lado de `.k`; `.k` ganha `flex-shrink:1` (mantém `min-width:150px` como piso, mas pode
   encolher/quebrar internamente se o próprio rótulo for mais largo que a tela, em vez de forçar
   sempre a largura total de uma única linha); `.v` ganha `min-width:0` (permite encolher abaixo do
   seu tamanho de conteúdo mínimo automático, necessário para o algoritmo de flex-wrap escolher
   quebrar para a linha seguinte) e `overflow-wrap:break-word` (blindagem contra um token único sem
   espaço, ex. um nome de conteúdo customizado colado sem quebras). Nenhuma mudança em telas
   largas: com espaço suficiente, o comportamento é idêntico ao de antes (uma linha, `.k` na
   largura do conteúdo/150px, `.v` ao lado). */
.kv-row{ display:flex; flex-wrap:wrap; gap:10px; padding:6px 2px; border-bottom:1px dashed var(--iron-soft); font-size:14px; }
.kv-row:last-child{ border-bottom:none; }
.kv-row .k{ color:var(--ash-dim); min-width:150px; flex-shrink:1; }
.kv-row .v{ color:var(--bone); min-width:0; flex-shrink:1; overflow-wrap:break-word; }

.wiz-review-grid{ margin-bottom:8px; }
.wiz-review-abilities{
  display:grid; grid-template-columns:repeat(3,1fr); gap:8px;
  margin:10px 0 16px; padding:10px 0; border-top:1px dashed var(--iron-soft); border-bottom:1px dashed var(--iron-soft);
}
@media (max-width:560px){ .wiz-review-abilities{ grid-template-columns:repeat(2,1fr); } }
/* TASK-204 (UC-040/ADR-042 § Decisão item 1): mesmo segundo degrau já aplicado a
   `.wiz-abilities-grid` (TASK-205) -- 2 colunas ainda pode "vazar" em telas bem estreitas
   quando o par rótulo/valor (ex. locale com abreviação mais longa + modificador negativo,
   "20 (-1)") não cabe ao lado de outro na mesma linha de 2 colunas. Mesmo breakpoint (480px,
   já usado nos critérios de aceite deste UC) dá a cada linha `.wiz-review-ability` a largura
   cheia da caixa. */
@media (max-width:480px){ .wiz-review-abilities{ grid-template-columns:1fr; } }
.wiz-review-ability{ display:flex; justify-content:space-between; font-size:13.5px; }
.wiz-review-ability .k{ color:var(--ash-dim); }

/* ---------- Rooms — "Minhas Salas" (TASK-172, UC-034, ADR-045) ----------
   Reaproveita ao máximo classes já existentes (.list-row/.name/.tag/.accordion/.add-btn/
   .icon-btn/.save-status/.header-portrait/.portrait-frame/.portrait-actions/
   .portrait-action-btn, mesmo padrão de custom-content.js/rooms.js::logoSectionHtml) -- só o
   essencial específico desta tela ganha classe nova: a miniatura de logo em tamanho de lista
   (menor que `.portrait-frame`, que já cobre o tamanho de formulário/cabeçalho de ficha), o
   botão de nome sem moldura própria (mesmo reset de `.spell-suggestion-name`) e os ícones de
   ação sem rótulo de texto (editar/excluir/compartilhar do Dono, sair do Membro -- requisito de
   produto não-negociável, UC-034 § Regras de negócio: `aria-label`/`title` cobrem
   acessibilidade, nunca texto visível). */
.room-list-logo-frame{
  width:36px; height:36px; border-radius:5px; overflow:hidden; flex-shrink:0;
  border:1px solid var(--iron-soft); background:var(--panel-2);
  display:flex; align-items:center; justify-content:center;
}
.room-list-logo-img{ width:100%; height:100%; object-fit:cover; display:block; }
.room-list-logo-placeholder{ font-size:16px; line-height:1; color:var(--ash-dim); }
.room-name-btn{
  background:none; border:none; padding:0; margin:0; font:inherit; font-weight:600;
  color:var(--bone); cursor:pointer; text-align:left;
}
.room-name-btn:hover{ color:var(--ember); text-decoration:underline; }
.room-icon-btn{
  background:none; border:1px solid var(--iron-soft); color:var(--ash-dim);
  border-radius:4px; width:28px; height:28px; display:inline-flex; align-items:center;
  justify-content:center; cursor:pointer; flex-shrink:0; transition:all .15s ease;
}
.room-icon-btn svg{ width:16px; height:16px; }
.room-icon-btn:hover{ border-color:var(--ember-dim); color:var(--ember); }
.room-icon-btn.danger:hover{ border-color:var(--blood-bright); color:var(--blood-bright); }
.wiz-review-ability .v{ color:var(--ember); font-family:'JetBrains Mono', monospace; }
