/* ============================================================================
 * mobile.css — camada de layout para SMARTPHONE.
 *
 * REGRA DE OURO (nao quebre isto):
 *   TODO seletor deste arquivo comeca com `html[data-viewport="mobile"]`
 *   (fork de LAYOUT, so celular) ou `html[data-pointer="coarse"]`
 *   (afordancia de TOQUE, celular + tablet).
 *   Sem esses atributos no <html>, este arquivo inteiro e' INERTE — e' assim
 *   que o layout de PC fica provadamente intocado, por construcao e nao por
 *   sorte de cascata. O script inline no <head> do index.html so' carimba
 *   data-viewport="mobile" quando o aparelho e' de toque E o menor lado da
 *   TELA (nao da janela) e' <= 600px.
 *
 * POR QUE ATRIBUTO E NAO @media:
 *   21 modulos JS injetam <style> no <head> em RUNTIME (ex.: expedicao.js:2231,
 *   passe.js:499, mestre_expedicao.js:1081). Eles entram DEPOIS deste <link>,
 *   entao num empate de especificidade eles ganham por ordem de fonte. Os
 *   seletores deles sao tipicamente (0,2,0); o prefixo de atributo aqui da
 *   (0,2,1) e vence sem precisar abrir nenhum daqueles arquivos.
 *
 * ESCADA quando (0,2,1) nao bastar:
 *   1. html[data-viewport="mobile"] .x                  -> (0,2,1)
 *   2. html[data-viewport="mobile"] .pai > .x           -> (0,3,1)
 *   3. html[data-viewport="mobile"][data-viewport] .x   -> (0,3,1) sem tocar no DOM
 *   4. !important — SO quando a regra base ja tem (sao 288 casos no style.css)
 *
 * NAO EDITE style.css por causa de mobile. Se um valor esta' errado no PC
 * tambem, isso e' bugfix de desktop e vai em commit separado.
 * ========================================================================== */


/* ==========================================================================
 * 0. TOKENS — so existem no celular
 * ========================================================================== */
html[data-viewport="mobile"] {
    /* Altura real do #top-bar, publicada por ResizeObserver no mobile_shell.js.
       O fallback so' vale no intervalo entre o 1o paint e o 1o observe. */
    --mob-topbar-h: 52px;
    /* Altura da barra de navegacao inferior (criada pelo mobile_shell.js). */
    --mob-nav-h: 56px;

    /* Area segura (notch / barra de gestos). Resolve 0px onde nao ha. */
    --mob-safe-t: env(safe-area-inset-top, 0px);
    --mob-safe-b: env(safe-area-inset-bottom, 0px);
    --mob-safe-l: env(safe-area-inset-left, 0px);
    --mob-safe-r: env(safe-area-inset-right, 0px);

    /* Alvo minimo de toque. 44px = Apple HIG; usamos como piso universal. */
    --mob-tap: 44px;

    /* Altura util de folha: dvh desconta a barra de endereco do navegador,
       coisa que 100vh NAO faz (e por isso o rodape dos modais vive cortado). */
    --mob-sheet-h: 92vh;
}
@supports (height: 100dvh) {
    html[data-viewport="mobile"] { --mob-sheet-h: 92dvh; }
}

/* --mob-tap tambem precisa existir no gate de TOQUE, e nao so' no de layout:
   um tablet (ou um PC 2-em-1 em modo tablet) e' `coarse` sem ser `mobile`, e
   la' as regras da secao 3 leem esta variavel. Sem esta declaracao o
   `var(--mob-tap)` fica invalido em tempo de computacao e a propriedade cai no
   valor INICIAL — o que nao e' "manter o que a cascata dizia", e' zerar. Foi
   assim que uma versao anterior deste arquivo apagou o `margin-left` do
   `.map-gear` (style.css:333) num PC de toque. */
html[data-pointer="coarse"] {
    --mob-tap: 44px;
}


/* ==========================================================================
 * 1. RESET DE TOQUE
 * ========================================================================== */
html[data-viewport="mobile"] {
    /* Impede o Safari de reflowar o texto por conta propria em rotacao. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

html[data-viewport="mobile"] body {
    /* Mata o pull-to-refresh do Chrome Android: rolar ate' o fim de uma lista
       dentro de modal recarregava o app e perdia o formulario. */
    overscroll-behavior: none;
    /* O body ja e' height:100vh no style.css; no celular isso conta a barra de
       endereco e sobra conteudo embaixo da dobra. dvh conta o visivel. */
    height: 100dvh;
    width: 100%;
}

/* Scroll de dentro de modal/lista nao "vaza" pro que esta' atras. */
html[data-viewport="mobile"] .char-modal,
html[data-viewport="mobile"] .mestrar-submodal,
html[data-viewport="mobile"] .mestre-form-body,
html[data-viewport="mobile"] .alternart-modal-body,
html[data-viewport="mobile"] .md-panel,
html[data-viewport="mobile"] #bestiary-content,
html[data-viewport="mobile"] #personagens-content,
html[data-viewport="mobile"] #region-panel-content {
    overscroll-behavior: contain;
}

html[data-pointer="coarse"] body {
    /* O retangulo cinza do WebKit em cima de botao dourado fica horrivel. */
    -webkit-tap-highlight-color: transparent;
}

/* Sem o `manipulation` sobra o duplo-toque-pra-zoom, que dispara sem querer
   em qualquer sequencia rapida de toques em botao. */
html[data-pointer="coarse"] button,
html[data-pointer="coarse"] a,
html[data-pointer="coarse"] [role="button"],
html[data-pointer="coarse"] .nav-btn,
html[data-pointer="coarse"] .menu-btn {
    touch-action: manipulation;
}


/* ==========================================================================
 * 2. FORMULARIOS — o defeito nº 1 do estudo
 *
 * 43 seletores de campo no style.css estao abaixo de 16px (contra 3 acima).
 * Abaixo de 16px o iOS Safari DA ZOOM ao focar, e como todo o app e'
 * position:fixed + body{overflow:hidden}, o zoom nao tem como ser desfeito:
 * o jogador fica preso numa tela ampliada e cortada. Acontece ja' no login.
 *
 * Uma regra resolve os 43 sem tocar em nenhum deles.
 * ========================================================================== */
html[data-viewport="mobile"] input,
html[data-viewport="mobile"] select,
html[data-viewport="mobile"] textarea {
    font-size: 16px;
}
/* Campos com font-size proprio e especificidade maior (classe) precisam do
   reforco — a lista cobre os seletores de campo do style.css. */
html[data-viewport="mobile"] .edit-input,
html[data-viewport="mobile"] .edit-textarea,
html[data-viewport="mobile"] .auth-input,
html[data-viewport="mobile"] .alternart-modal-input,
html[data-viewport="mobile"] .admin-field-input,
html[data-viewport="mobile"] .mestrar-submodal-input,
html[data-viewport="mobile"] .mestrar-submodal-textarea,
html[data-viewport="mobile"] .mestrar-submodal-select,
html[data-viewport="mobile"] .mestre-form-body input,
html[data-viewport="mobile"] .mestre-form-body select,
html[data-viewport="mobile"] .mestre-form-body textarea,
html[data-viewport="mobile"] .mestre-painel-body input,
html[data-viewport="mobile"] .mestre-painel-body select,
html[data-viewport="mobile"] .mestre-painel-body textarea,
html[data-viewport="mobile"] .hab-form-content input,
html[data-viewport="mobile"] .hab-form-content select,
html[data-viewport="mobile"] .hab-form-content textarea,
html[data-viewport="mobile"] .mestrar-modal input,
html[data-viewport="mobile"] .mestrar-modal select,
html[data-viewport="mobile"] .mestrar-modal textarea,
html[data-viewport="mobile"] .pgjump-input,
html[data-viewport="mobile"] .char-armazem-search,
html[data-viewport="mobile"] .char-armazem-sort-select,
html[data-viewport="mobile"] .md-apelido-input,
html[data-viewport="mobile"] .md-info-textarea,
html[data-viewport="mobile"] .setting-select,
html[data-viewport="mobile"] .conexao-add-select,
html[data-viewport="mobile"] .bf-attr-pecas-select,
html[data-viewport="mobile"] .exp-char-select,
html[data-viewport="mobile"] .mestre-passe-filtro-select,
html[data-viewport="mobile"] .mestrar-bn-input,
html[data-viewport="mobile"] .mestrar-bn-select-narrow,
html[data-viewport="mobile"] select.char-pot-card-af-sel,
html[data-viewport="mobile"] .passe-tier-kills-input,
html[data-viewport="mobile"] .passe-wallpaper-input,
html[data-viewport="mobile"] .bf-attr-pecas-select {
    font-size: 16px;
}

/* Campo confortavel de tocar. min-height, nao height: nao quebra textarea. */
html[data-pointer="coarse"] input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
html[data-pointer="coarse"] select {
    min-height: var(--mob-tap, 44px);
}
/* Checkbox de 14px e' impossivel de acertar; 22px ja' resolve sem estourar
   linha de formulario. */
html[data-pointer="coarse"] input[type="checkbox"],
html[data-pointer="coarse"] input[type="radio"] {
    width: 22px;
    height: 22px;
}


/* ==========================================================================
 * 3. ALVOS DE TOQUE
 *
 * 94% dos alvos do app estao abaixo de 44px, e as media queries existentes
 * ENCOLHEM ainda mais no mobile em vez de crescer: a de 768px leva o
 * .nav-btn de `padding:8px 16px` pra `6px 10px` e a fonte de 0.85 pra
 * 0.75rem (style.css:1722), deixando o botao com ~32px de altura.
 * Aqui a direcao e' invertida.
 *
 * Nao mexemos no tamanho do ICONE — so' na area clicavel.
 * ========================================================================== */
html[data-pointer="coarse"] .nav-btn,
html[data-pointer="coarse"] .menu-btn,
html[data-pointer="coarse"] .panel-close-btn,
html[data-pointer="coarse"] .char-modal-close,
html[data-pointer="coarse"] .alternart-modal-close,
html[data-pointer="coarse"] .mestrar-submodal-close,
html[data-pointer="coarse"] .mestre-form-close,
html[data-pointer="coarse"] #region-panel-close,
html[data-pointer="coarse"] .quests-modal-close,
html[data-pointer="coarse"] .passe-modal-close,
html[data-pointer="coarse"] .md-tab,
html[data-pointer="coarse"] .char-tab,
html[data-pointer="coarse"] .nav-char-btn,
html[data-pointer="coarse"] .nav-grp,
html[data-pointer="coarse"] .nav-mundo-arrow,
html[data-pointer="coarse"] .leaflet-control-zoom a,
html[data-pointer="coarse"] .bestiary-pagination button,
html[data-pointer="coarse"] .mestrar-bn-roster-item,
html[data-pointer="coarse"] .idtl-qty-btn,
html[data-pointer="coarse"] .char-point-btn,
html[data-pointer="coarse"] .tt-info-btn,
html[data-pointer="coarse"] .ed-close,
html[data-pointer="coarse"] .exp-pick-close,
html[data-pointer="coarse"] .mestrar-end-close,
html[data-pointer="coarse"] .mestre-painel-close,
html[data-pointer="coarse"] .passe-detail-close,
html[data-pointer="coarse"] .passe-settings-close {
    min-width: var(--mob-tap, 44px);
    min-height: var(--mob-tap, 44px);
}
/* Centraliza o glifo dentro do alvo maior, senao o icone fica encostado.
   NAO inclua `.leaflet-control-zoom a` aqui: o Leaflet gera os dois <a> sem
   nenhum no' de texto entre eles, entao trocar `display:block` por
   `inline-flex` poe o + e o - LADO A LADO em vez de empilhados. O zoom e'
   tratado logo abaixo, pelo caminho que o proprio Leaflet usa (line-height). */
html[data-pointer="coarse"] .char-modal-close,
html[data-pointer="coarse"] .alternart-modal-close,
html[data-pointer="coarse"] .mestrar-submodal-close,
html[data-pointer="coarse"] #region-panel-close,
html[data-pointer="coarse"] .tt-info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Zoom do mapa: a regra base (style.css:506-514) crava 36px com !important em
   width/height/line-height, entao aqui precisa de !important tambem — e
   mantendo o `display:block` do Leaflet, pelo motivo do comentario acima. */
html[data-pointer="coarse"] .leaflet-control-zoom a,
html[data-pointer="coarse"] .map-icons-toggle a {
    width: var(--mob-tap, 44px) !important;
    height: var(--mob-tap, 44px) !important;
    line-height: var(--mob-tap, 44px) !important;
}

/* NAO ha regra aqui pra `.map-star-premio` / `.map-gear`.
   A tentativa obvia (min-width/min-height + margem negativa) e' INUTIL e
   PERIGOSA: inutil porque o ancestral tem `scale: var(--map-icon-scale)`
   (style.css:601), que vale 0.5 no zoom minimo e encolhe a area de hit-test
   junto com o desenho — um alvo de 44px renderiza como 22px; e perigosa
   porque a `margin` shorthand apaga o `margin-left: 0.32em` deliberado do
   `.map-gear` (style.css:333) e cola a engrenagem no nome da ilha.
   O conserto de verdade e' um piso pro --map-icon-scale em `applyIconScale()`
   (app.js:1305), que e' JS e esta' na E3 do estudo — nao foi feito. */


/* ==========================================================================
 * 4. HOVER — 404 regras :hover contra 1 unico @media(hover:none) no style.css
 * ========================================================================== */

/* 4a. Cinco acoes que SO existem no hover: no toque elas nao existem.
       A pior e' remover item da mochila. */
html[data-pointer="coarse"] .mochila-slot-remove,
html[data-pointer="coarse"] .card-delete-btn,
html[data-pointer="coarse"] .char-select-card-delete,
html[data-pointer="coarse"] .monstro-ativo-remove,
html[data-pointer="coarse"] .treinador-cap-edit,
html[data-pointer="coarse"] .mestrar-resource-add,
html[data-pointer="coarse"] .region-char-switch-icon,
html[data-pointer="coarse"] .char-monster-remove,
html[data-pointer="coarse"] .mestrar-armazem-card-rm {
    display: flex !important;
    opacity: 1 !important;
    visibility: visible !important;
}
/* Duas telas escondem esses mesmos botoes DE PROPOSITO (nao e' hover): o
   picker do Ritual de Reset (ritual_reset.js:103) e o clone em destaque da
   animacao de transferencia (style.css:22304). Sem esta excecao o !important
   acima ressuscitaria um X inerte nos dois. */
html[data-pointer="coarse"] .rrm-slot .char-monster-remove,
html[data-pointer="coarse"] .rrm-slot .char-monster-dice,
html[data-pointer="coarse"] .transferencia-spotlight-clone .char-monster-remove {
    display: none !important;
}

/* 4b. Hover "grudento" (o botao fica preso parecendo selecionado depois do
       toque) NAO esta' tratado aqui, de proposito.
       A tentativa `*:hover{transition-duration:0}` nao resolve — zerar a
       transicao so' faz o estado grudado aparecer instantaneamente, ele
       continua grudado — e ainda aplicaria um seletor universal com
       !important no app inteiro, atingindo todo ancestral do elemento tocado.
       O conserto correto e' envelopar as 404 regras `:hover` do style.css em
       `@media (hover: hover)`, o que significa editar o style.css. Fica
       registrado como pendencia, nao como feito. */


/* ==========================================================================
 * 5. SHELL — barra superior, mapa, barra inferior, drawer
 * ========================================================================== */

/* 5a. Top-bar: uma faixa compacta. O #nav-right e' esvaziado pelo
       mobile_shell.js (botoes vao pra barra inferior e pro drawer), entao o
       que sobra aqui e' Voltar + titulo + seletor de personagem, mais o
       #nav-status (HUD de energia) na 2a linha. */
html[data-viewport="mobile"] #top-bar {
    min-height: 0;
    padding: calc(6px + var(--mob-safe-t)) 8px 6px;
    column-gap: 8px;
    row-gap: 4px;
    /* backdrop-filter: blur() numa barra fixa sobre um mapa Leaflet que
       repinta a cada arrasto e' dos efeitos mais caros em GPU de celular. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: rgba(26, 20, 16, 0.97);
}
html[data-viewport="mobile"] #nav-title {
    font-size: 1rem;
    letter-spacing: 0.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
html[data-viewport="mobile"] #nav-compass {
    width: 24px;
    height: 24px;
    /* Bussola girando o tempo todo = repaint permanente. */
    animation: none;
}
/* O HUD de energia ocupa a 2a linha inteira, com altura propria de toque. */
html[data-viewport="mobile"] #nav-status {
    flex: 1 1 100%;
    justify-content: center;
    min-height: var(--mob-tap);
}

/* 5b. Mapa: o #map do style.css tem `top: 60px` cravado, contra uma barra que
       no celular mede outra coisa. Aqui ele passa a consumir a altura REAL,
       publicada pelo ResizeObserver. O PC continua nos 60px — esta regra nao
       existe la'. */
html[data-viewport="mobile"] #map {
    top: var(--mob-topbar-h);
    bottom: calc(var(--mob-nav-h) + var(--mob-safe-b));
}

/* 5c. Controles de zoom: do canto superior esquerdo (onde ficam debaixo da
       barra) pro inferior direito, ao alcance do polegar. O `!important` e'
       obrigatorio: a regra base (.leaflet-top.leaflet-left) ja' tem um. */
html[data-viewport="mobile"] .leaflet-top.leaflet-left {
    top: auto !important;
    bottom: calc(var(--mob-nav-h) + var(--mob-safe-b) + 12px);
    left: auto;
    right: 8px;
}
html[data-viewport="mobile"] .leaflet-top.leaflet-left .leaflet-control {
    margin-left: 0;
    margin-right: 0;
}

/* 5d. Barra de navegacao inferior — elemento NOVO, criado pelo mobile_shell.js.
       Recebe os botoes que ja' existiam no #nav-right (movidos, nao clonados:
       clonar perderia os handlers ligados pelo app.js). */
html[data-viewport="mobile"] .mob-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1200;
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    gap: 2px;
    height: calc(var(--mob-nav-h) + var(--mob-safe-b));
    padding: 0 4px var(--mob-safe-b);
    background: rgba(26, 20, 16, 0.98);
    border-top: 1px solid rgba(201, 168, 76, 0.35);
    box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.5);
}
/* `display:flex` acima venceria o display:none que o atributo [hidden] traz do
   user-agent — por isso a regra explicita. O mobile_shell.js espelha aqui o
   estado do #top-bar, pra barra nao flutuar sobre o menu principal. */
html[data-viewport="mobile"] .mob-nav[hidden] { display: none; }
/* Os .nav-btn movidos pra ca' herdam o visual da barra de cima; aqui viram
   slots iguais de largura, com alvo de 56px. */
html[data-viewport="mobile"] .mob-nav .nav-btn {
    flex: 1 1 0;
    min-width: 0;
    min-height: 56px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 0;
    border: none;
    background: transparent;
}
html[data-viewport="mobile"] .mob-nav .nav-btn svg {
    width: 22px;
    height: 22px;
}
/* Badge de quests/passe continua funcionando: e' posicionado em relacao ao
   proprio botao, que se mudou de lugar junto. */
/* `:not(.nav-btn-badge-notif)`: o badge de recebimentos da cabecinha tem
   posicao propria e deliberada (style.css:14120) e nao deve ser recolocado. */
html[data-viewport="mobile"] .mob-nav .nav-btn-badge:not(.nav-btn-badge-notif) {
    top: 6px;
    /* `bottom: auto` e' obrigatorio: a base declara `bottom:-4px`
       (style.css:14095) e, com top E bottom definidos e height:auto, o badge
       estica pra preencher o botao de 56px — vira uma capsula vertical. O
       proprio style.css ja' conhece a armadilha e faz o mesmo em
       `.nav-btn-badge-notif` (style.css:14124). */
    bottom: auto;
    right: calc(50% - 18px);
}
/* Botao de teste de layout do topo: no celular ele nao faz mais nada (o CSS
   dele mira `#top-bar[data-layout] .nav-btn`, e os botoes sairam do #top-bar)
   e apareceria no drawer com o rotulo por extenso pra qualquer jogador. */
html[data-viewport="mobile"] #btn-layout-test {
    display: none !important;
}

/* 5e. Drawer "⋯ Mais" — bottom-sheet com o resto dos botoes (Guia, Atualizar,
       Opcoes/Sair) e, pro narrador, os [data-editor-only]. Os botoes que o JS
       mantem em display:none continuam escondidos aqui dentro, o que e'
       exatamente o comportamento certo. */
html[data-viewport="mobile"] .mob-drawer-bd {
    position: fixed;
    inset: 0;
    z-index: 1250;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: flex-end;
}
html[data-viewport="mobile"] .mob-drawer-bd[hidden] { display: none; }
html[data-viewport="mobile"] .mob-drawer {
    width: 100%;
    max-height: 70vh;
    max-height: 70dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: linear-gradient(160deg, #1a120a 0%, #100905 100%);
    border-top: 1px solid rgba(201, 168, 76, 0.55);
    border-radius: 16px 16px 0 0;
    padding: 8px 10px calc(12px + var(--mob-safe-b));
}
html[data-viewport="mobile"] .mob-drawer-handle {
    width: 40px;
    height: 4px;
    margin: 4px auto 10px;
    border-radius: 2px;
    background: rgba(201, 168, 76, 0.45);
}
html[data-viewport="mobile"] .mob-drawer-title {
    font-family: 'Cinzel', serif;
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: rgba(232, 216, 184, 0.55);
    padding: 10px 6px 4px;
    border-top: 1px dashed rgba(201, 168, 76, 0.2);
    margin-top: 6px;
}
/* `:first-of-type` NAO serve aqui: e' por tipo de elemento (div), e o primeiro
   div do drawer e' o handle. O seletor de irmao adjacente e' o que casa. */
html[data-viewport="mobile"] .mob-drawer-handle + .mob-drawer-title {
    border-top: none;
    margin-top: 0;
}
/* Cada botao vira uma linha larga e rotulada. O rotulo sai do title=""
   (que no toque nao aparece) e e' escrito num <span> pelo mobile_shell.js. */
html[data-viewport="mobile"] .mob-drawer .nav-btn {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;
    width: 100%;
    min-height: 48px;
    padding: 0 12px;
    margin: 2px 0;
    border-radius: 8px;
    font-size: 0.95rem;
}
html[data-viewport="mobile"] .mob-drawer .nav-btn svg {
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
}
html[data-viewport="mobile"] .mob-drawer-label {
    font-family: 'Lora', serif;
    letter-spacing: 0.02em;
}
/* Botao desabilitado no drawer nao vira linha larga rotulada. Hoje isso pega
   o #btn-quests (quests.js:257 desabilita enquanto a feature esta' desligada),
   cujo rotulo foi copiado do title ANTES de o quests.js reescreve-lo — sairia
   "Quests Diarias", soando vivo. A regra e' auto-corretiva: quando a feature
   voltar, o botao deixa de ser `disabled` e reaparece sozinho. */
html[data-viewport="mobile"] .mob-drawer .nav-btn:disabled {
    display: none;
}
/* Botao "⋯" que abre o drawer — criado pelo mobile_shell.js. */
html[data-viewport="mobile"] .mob-burger {
    flex: 1 1 0;
    min-height: 56px;
}


/* ==========================================================================
 * 6. MENU PRINCIPAL
 *
 * O menu e' flex-centrado sem overflow, dentro de body{overflow:hidden}.
 * Com 7 botoes (narrador) o conteudo passa de 730px e o que sobra fica
 * inalcancavel — nao ha scroll em lugar nenhum da cadeia.
 * ========================================================================== */
html[data-viewport="mobile"] #main-menu {
    align-items: flex-start;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: calc(20px + var(--mob-safe-t)) 0 calc(24px + var(--mob-safe-b));
}
html[data-viewport="mobile"] .menu-content {
    width: 90%;
    max-width: 420px;
    margin: auto;
}
html[data-viewport="mobile"] .menu-logo {
    width: 72px;
    height: 72px;
    margin-bottom: 10px;
}
/* "TECHTERRA ONLINE" em Cinzel Decorative 900 a 2.6rem com tracking de 6px
   passa de 330px numa caixa de 324px — e como e' palavra unica, nao quebra:
   corta. O clamp resolve sem mexer no desktop (la' a regra nem existe). */
html[data-viewport="mobile"] .menu-title {
    font-size: clamp(1.4rem, 7.5vw, 2.6rem);
    letter-spacing: 3px;
    word-break: break-word;
}
html[data-viewport="mobile"] .menu-subtitle {
    margin-bottom: 20px;
}
html[data-viewport="mobile"] .menu-btn {
    min-height: 48px;
    margin: 7px 0;
}
/* A versao e' position:absolute com bottom:-60px — pendurada fora da tela. */
html[data-viewport="mobile"] .menu-version {
    position: static;
    margin-top: 22px;
    text-align: center;
}


/* ==========================================================================
 * 7. PAINEIS EM TELA CHEIA
 *
 * Os paineis sao caixas centradas com max-height em `vh`. No celular vale
 * mais a tela toda — e `dvh` no lugar de `vh` impede que o rodape fique
 * debaixo da barra de endereco.
 * ========================================================================== */
html[data-viewport="mobile"] #bestiary-content,
html[data-viewport="mobile"] #personagens-content,
html[data-viewport="mobile"] #habilidades-content,
html[data-viewport="mobile"] #region-panel-content {
    top: 0;
    left: 0;
    transform: none;
    width: 100%;
    max-width: none;
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
    border-left: none;
    border-right: none;
    padding-top: var(--mob-safe-t);
    padding-bottom: var(--mob-safe-b);
}
/* A ficha ja' tem uma variante fullscreen propria a partir de 768px; aqui so'
   trocamos a unidade de altura, que e' o que corta o rodape no iOS. */
html[data-viewport="mobile"] #personagens-content.char-fullscreen {
    height: 100dvh;
    max-height: 100dvh;
}
/* Gaveta do Guia: 420px numa tela de 360px vazava pela esquerda e o botao de
   fechar ficava fora. */
html[data-viewport="mobile"] #guide-panel {
    width: 100%;
    height: 100dvh;
    padding-bottom: var(--mob-safe-b);
}
/* Sidebar do editor de mapa (narrador): 400px em 360px de tela. */
html[data-viewport="mobile"] .map-editor-sidebar {
    width: 100%;
}
html[data-viewport="mobile"] #map-editor-panel {
    top: var(--mob-topbar-h);
}


/* ==========================================================================
 * 8. MODAIS -> BOTTOM-SHEET
 *
 * Existem 20 cascas de modal no app, mas 4 delas cobrem ~97 dos ~104 pontos
 * de montagem. Estas 4 sao as que valem a pena converter.
 *
 * Ancorar embaixo nao e' estetica: e' alcance do polegar. E como a folha
 * ocupa a largura toda, o backdrop deixa de existir como rota de fuga — por
 * isso o X ganhou 44px na secao 3.
 * ========================================================================== */
html[data-viewport="mobile"] .char-modal-backdrop,
html[data-viewport="mobile"] .mestre-form-overlay,
html[data-viewport="mobile"] .mestrar-submodal-bd,
html[data-viewport="mobile"] .alternart-modal-overlay {
    align-items: flex-end;
    padding: 0;
}
html[data-viewport="mobile"] .char-modal,
html[data-viewport="mobile"] .mestre-form-modal,
html[data-viewport="mobile"] .mestrar-submodal,
html[data-viewport="mobile"] .alternart-modal {
    width: 100%;
    max-width: none;
    max-height: var(--mob-sheet-h);
    border-radius: 14px 14px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    padding-bottom: calc(16px + var(--mob-safe-b));
}
/* O .char-modal tinha max-height:70vh — 30% de tela morta com o conteudo
   rolando num tubo estreito. */
html[data-viewport="mobile"] .char-modal {
    padding: 16px 14px calc(16px + var(--mob-safe-b));
}
/* .mestre-form-modal e' overflow:hidden com corpo rolante interno: a altura
   precisa ser limitada nele, nao no corpo. */
html[data-viewport="mobile"] .mestre-form-body {
    max-height: none;
}
/* A ficha de NPC e' a excecao das 4 cascas: style.css:17050-17052 (dentro de
   @media 768px) crava `width:100vw !important; max-height:100vh !important`.
   O `100vh` e' exatamente o defeito que a folha vem consertar — no iOS o
   rodape fica debaixo da barra de endereco. So' !important alcanca. */
html[data-viewport="mobile"] .mestre-form-modal.npc-ficha-modal {
    width: 100% !important;
    max-width: none !important;
    height: var(--mob-sheet-h) !important;
    max-height: var(--mob-sheet-h) !important;
}


/* ==========================================================================
 * 9. ABAS ROLAVEIS
 *
 * .md-tabs e' flex sem wrap E sem overflow: as 4 abas somam ~484px, o padding
 * nao encolhe, e cada aba quebra em 3-4 linhas cortando palavra.
 * A solucao ja' existia no repo (.char-sheet-tabs) — e' a mesma receita.
 * ========================================================================== */
html[data-viewport="mobile"] .md-tabs,
html[data-viewport="mobile"] .char-sheet-tabs,
html[data-viewport="mobile"] .mestre-painel-tabs,
html[data-viewport="mobile"] .sorteio-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    justify-content: flex-start;
    scroll-snap-type: x proximity;
    /* Barra de rolagem visivel mudaria a altura da faixa de abas. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}
html[data-viewport="mobile"] .md-tabs::-webkit-scrollbar,
html[data-viewport="mobile"] .char-sheet-tabs::-webkit-scrollbar,
html[data-viewport="mobile"] .mestre-painel-tabs::-webkit-scrollbar,
html[data-viewport="mobile"] .sorteio-tabs::-webkit-scrollbar {
    display: none;
}
html[data-viewport="mobile"] .md-tab,
html[data-viewport="mobile"] .char-tab,
html[data-viewport="mobile"] .mestre-tab,
html[data-viewport="mobile"] .sorteio-tab {
    flex: 0 0 auto;
    white-space: nowrap;
    scroll-snap-align: start;
}


/* ==========================================================================
 * 10. GRADES DE CARD
 *
 * .bestiary-grid e' `repeat(5, minmax(0,1fr))` FIXO e nao tem override global.
 * Em 360px isso da' cards de ~47px: o auto-fit de nome (fitOneCardName) desce
 * ate' 8px e depois ABREVIA o nome em iniciais.
 *
 * ATENCAO: a regra base do style.css NAO pode ser tocada — a paginacao de
 * 10/pagina (5x2) e o comentario em style.css:3782 dependem dela no PC.
 * ========================================================================== */
html[data-viewport="mobile"] .bestiary-grid,
html[data-viewport="mobile"] .monster-picker-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
}
html[data-viewport="mobile"] .monstros-ativos-grid,
html[data-viewport="mobile"] .monstros-armazem-grid,
html[data-viewport="mobile"] #personagens-content.char-fullscreen .monstros-ativos-grid,
html[data-viewport="mobile"] #personagens-content.char-fullscreen .monstros-armazem-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
html[data-viewport="mobile"] .mochila-grid,
html[data-viewport="mobile"] #personagens-content.char-fullscreen .mochila-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
/* A grade de monstros do NPC leva 3 classes ao mesmo tempo
   (`monstros-armazem-grid npc-monstros-grid npc-monstros-paginated`,
   mestre_painel.js:7973 + :8462) e a regra de style.css:17647 usa
   `!important` — colocado la' justamente pra vencer overrides de media query.
   Sem casar o !important, a grade fica em 5 colunas no celular. */
html[data-viewport="mobile"] .npc-monstros-grid.npc-monstros-paginated,
html[data-viewport="mobile"] #f-monsters-list.npc-monstros-paginated {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
}
html[data-viewport="mobile"] .char-pot-grid,
html[data-viewport="mobile"] .char-attr-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
html[data-viewport="mobile"] .city-shop-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* Barra de acoes do bestiario: busca + filtro + "novo monstro" numa linha so'
   espremem o input a quase nada. */
html[data-viewport="mobile"] .bestiary-actions {
    flex-wrap: wrap;
}
/* `.req-row` ficou ORFA: a linha de requisito do item virou gaveta
   (spec-componente-gaveta) e o corpo dela ja reflui sozinho na `.ef-grid`.
   Nao apagar agora — a limpeza (aqui e no bloco `.req-row` de `ui/style.css`)
   fica pra E8. */
html[data-viewport="mobile"] .bestiary-pagination,
html[data-viewport="mobile"] .req-row,
html[data-viewport="mobile"] .mestre-passe-toolbar {
    flex-wrap: wrap;
}
/* Dropdown de filtro com min-width:280px dentro de um container de 279px
   dispara scroll horizontal. */
html[data-viewport="mobile"] .bestiary-filter-dropdown {
    min-width: 0;
    width: min(92vw, 320px);
    right: 0;
}
/* Popup de "pular pra pagina" abre PRA CIMA e com o teclado virtual sai da
   tela; 244px fixos tambem estouram. */
html[data-viewport="mobile"] .pgjump-pop {
    width: min(92vw, 244px);
    max-width: 92vw;
}

/* Os dois seletores de card (template — card_bg_test.js:205; animacao —
   card_anim.js:232) sao `position:absolute; right:0` e `right:44px` dentro da
   barra de paginacao, entao NAO reservam espaco no fluxo. No PC sobram ~216px
   de folga de cada lado e ninguem ve; em 390px sobram ~11px e as duas
   engrenagens caem em cima do "Proximo »" — que e' exatamente "o proximo" do
   relato. Aqui elas voltam pro fluxo e viram itens da linha, que ja' quebra
   pela regra de `flex-wrap` acima.
   `transform:none` e' obrigatorio junto: `top/right` sao ignorados em
   `position:static`, mas `transform` nao — sem isso continuariam 50% pra cima.
   O z-index segue valendo porque em `static` dentro de um flex container o
   elemento e' flex item, e flex item respeita z-index. */
html[data-viewport="mobile"] #bestiary-pagination .cardbg-gear-wrap,
html[data-viewport="mobile"] #bestiary-pagination .cardanim-wrap {
    position: static;
    transform: none;
}
/* Os dropdowns desses dois tem largura minima propria (230/256px) declarada no
   <style> injetado, com especificidade (1,1,0) — a regra generica de
   `.bestiary-filter-dropdown` acima nao alcanca. */
html[data-viewport="mobile"] #bestiary-pagination .cardbg-dropdown,
html[data-viewport="mobile"] #bestiary-pagination .cardanim-dropdown {
    min-width: 0;
    width: min(92vw, 300px);
}


/* ==========================================================================
 * 11. DETALHE DO MONSTRO — tabela vira card
 *
 * .md-stat-row e' um grid de 7 colunas com 400px de minimo, dentro de uma
 * caixa de ~276px. Rolava na horizontal POR ACIDENTE (o overflow-y do painel
 * faz o eixo X virar auto), sem nenhuma affordance, e a coluna cortada era
 * justo o Total. O mesmo vale pra .md-hab-row (362px), onde o que sumia era a
 * coluna de Acoes — ou seja, o switch da passiva.
 *
 * Conversao: flex com quebra forcada por ::after (flex-basis:100%), que poe
 * nome+total na 1a linha e os numeros miudos na 2a. A ordem do DOM e' fixa e
 * conhecida (verificada em monster_detail.js:375 e :660).
 * ========================================================================== */
html[data-viewport="mobile"] .md-layout {
    grid-template-columns: 1fr;
    overflow: visible;
}
/* Empilhado, `sticky` no topo atrapalha em vez de ajudar. */
html[data-viewport="mobile"] .md-side {
    position: static;
}
/* O card quadrado do monstro ocupa a largura toda (=276px de altura so' de
   imagem). Vira faixa horizontal compacta. */
html[data-viewport="mobile"] .md-card-wrap {
    max-width: 190px;
    margin: 0 auto;
}

/* --- atributos --- */
html[data-viewport="mobile"] .md-stats-header,
html[data-viewport="mobile"] .md-habs-header {
    /* Cabecalho de tabela nao faz sentido quando cada linha virou card. */
    display: none;
}
/* A conversao usa `grid-template-areas`, e NAO flex com `order` + um
   `::after{flex-basis:100%}` pra forcar a quebra de linha. Motivo concreto:
   `.md-hab-row.md-hab-row-passiva::after` (style.css:30344) ja' e' um
   pseudo-elemento `position:absolute` (as ranhuras diagonais da passiva), e
   `.md-hab-row-passiva-ativa::before` (style.css:30382) ocupa o outro. Um
   `::after` nosso venceria a cascata sem restaurar `position`, e como
   elemento absoluto nao e' item de flex, a quebra simplesmente nao
   aconteceria — justo nas linhas de passiva. Grid nao depende de pseudo:
   cada filho tem classe unica e vai direto pra sua area.
   Ordem do DOM verificada em monster_detail.js:375 (7 filhos) e :660 (6). */
html[data-viewport="mobile"] .md-stat-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    grid-template-areas:
        /* a area chama-se `varia` e nao `var`: `var` como <custom-ident> e'
           valido, mas confunde leitor e ferramenta que procura var() */
        "tier  nome  nome  total"
        "base  varia lvl   equip";
    align-items: center;
    gap: 2px 8px;
    padding: 8px;
    margin-bottom: 6px;
    border: 1px solid rgba(201, 168, 76, 0.18);
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.22);
}
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-tier  { grid-area: tier; }
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-nome  { grid-area: nome; font-size: 0.92rem; }
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-total {
    grid-area: total;
    justify-self: end;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--gold-light, #f3da9c);
}
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-base  { grid-area: base; }
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-var   { grid-area: varia; }
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-lvl   { grid-area: lvl; }
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-equip { grid-area: equip; }
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-base,
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-var,
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-lvl,
html[data-viewport="mobile"] .md-stat-row > .md-stat-col-equip {
    font-size: 0.72rem;
    opacity: 0.85;
    /* `text-align` nao teria efeito: as celulas sao display:flex com
       justify-content:center na base (style.css:29990). */
    justify-content: flex-start;
}

/* --- habilidades --- */
html[data-viewport="mobile"] .md-hab-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "tipo   nome   acoes"
        "poder  custo  status";
    align-items: center;
    gap: 2px 8px;
    padding: 8px;
    margin-bottom: 6px;
    border-radius: 8px;
}
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-tipo   { grid-area: tipo; }
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-nome   { grid-area: nome; font-size: 0.92rem; }
/* A coluna de Acoes carrega o switch da passiva — no corte horizontal do PC
   ela era justamente a que sumia. Aqui ela fica na 1a linha, sempre visivel. */
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-acoes  { grid-area: acoes; justify-self: end; }
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-poder  { grid-area: poder; }
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-custo  { grid-area: custo; }
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-status { grid-area: status; }
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-poder,
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-custo,
html[data-viewport="mobile"] .md-hab-row > .md-hab-col-status {
    font-size: 0.72rem;
    opacity: 0.85;
    justify-content: flex-start;
}

/* --- equipamento --- */
html[data-viewport="mobile"] .md-equip-grid {
    grid-template-columns: 1fr;
}

/* --- equipamento v2: cartas relicario (spec-equipamento-6-slots E6) ---
 * Toque nao tem hover: a carta has-many mostra so' brief + pill e o clique
 * abre o ItemDetail (JS cuida). Aqui: empilha as cartas, devolve o scroll
 * ao painel, halo menor e acoes sempre visiveis. */
html[data-viewport="mobile"] .md-panel[data-panel="equipamento"]:has(.md-eqc-row) {
    overflow-y: auto;
}
html[data-viewport="mobile"] .md-eqc-row {
    flex-direction: column;
    flex: 0 0 auto;
    min-height: 0;
    height: auto;
}
html[data-viewport="mobile"] .md-eqc-card {
    flex: 0 0 auto;
    min-height: 0;
}
/* Neutraliza o hover "grudado" que navegador de toque emula no tap:
 * nada de lift nem de expandir; brief/halo ficam e o full nunca abre. */
html[data-viewport="mobile"] .md-eqc-card:hover {
    transform: none;
}
html[data-viewport="mobile"] .md-eqc-card.has-many:hover {
    flex: 0 0 auto;
    border-radius: 200px 200px 10px 10px;
}
html[data-viewport="mobile"] .md-eqc-card.has-many:hover .md-eqc-brief { display: flex; }
html[data-viewport="mobile"] .md-eqc-card.has-many:hover .md-eqc-halo { display: flex; }
html[data-viewport="mobile"] .md-eqc-card.has-many:hover .md-eqc-full { display: none; }
html[data-viewport="mobile"] .md-eqc-halo {
    width: 48px;
    height: 48px;
    margin-top: 4px;
}
html[data-viewport="mobile"] .md-eqc-halo img {
    width: 44px;
    height: 44px;
}
/* Acoes (trocar/desequipar) nao podem depender de hover no toque */
html[data-viewport="mobile"] .md-eqc-acts { display: flex; }
/* FRONT-F9 (revisao final): no mobile o full nunca abre, entao o pill nao
 * promete expansao — vira contador estatico (sem pulso). A lista completa
 * sai pelo toque na carta (ItemDetail). */
html[data-viewport="mobile"] .md-eqc-pill { animation: none; }
html[data-viewport="mobile"] .md-eqc-card.has-many:hover .md-eqc-acts { top: 28px; }
html[data-viewport="mobile"] .md-eqc-card-readonly .md-eqc-acts { display: none; }


/* ==========================================================================
 * 12. ITEM, NPC, ESPECIE, ARMAZEM
 * ========================================================================== */

/* A arte do item e' position:absolute e cobre 70px do titulo. Empilhado. */
html[data-viewport="mobile"] .idtl-hero {
    min-height: 0;
    text-align: center;
}
html[data-viewport="mobile"] .idtl-hero-art {
    position: static;
    right: auto;
    transform: none;
    margin: 0 auto 8px;
}
html[data-viewport="mobile"] .idtl-img {
    max-width: 120px;
}
html[data-viewport="mobile"] .idtl-hero-info {
    max-width: 100%;
}
html[data-viewport="mobile"] .idtl-status-cols {
    grid-template-columns: 1fr;
}

/* NPC: --npcd-w e' 210px de retrato numa caixa de 276px, sobrando 48px pra
   TODA a coluna de conteudo. E nao havia @media nenhuma pra isso. */
html[data-viewport="mobile"] .npc-detail {
    --npcd-w: 100%;
    --npcd-h: auto;
}
html[data-viewport="mobile"] .npcd-layout {
    grid-template-columns: 1fr;
}

/* Armazem: dois scrolls verticais empilhados (grid 48vh dentro de modal 70vh).
   No toque o dedo sobre a grade nunca rola o modal, e o rodape de acoes fica
   inalcancavel. Um scroll so'. */
html[data-viewport="mobile"] .armazem-grid {
    max-height: none;
    overflow-y: visible;
}
html[data-viewport="mobile"] .char-mochila-layout {
    grid-template-columns: 1fr;
}


/* ==========================================================================
 * 13. BATALHA
 *
 * O defeito mais grave do estudo: a arena NUNCA empilhou porque a regra que
 * deveria fazer isso (style.css:28883) mira `.mestrar-bn-stage`, classe que
 * nao existe em lugar nenhum do repositorio. A classe real e'
 * `.mestrar-bn-arena` — emitida tanto pela batalha SELVAGEM
 * (battle_backend.js:1147) quanto pela NARRADA (mestrar_session.js:4135).
 *
 * Nao consertamos a linha 28883: ela vive num @media de 900px, que pega
 * laptop de PC em janela restaurada. Aqui a correcao e' escopada no celular.
 * ========================================================================== */

/* O modal da batalha tem width com !important — precisa de !important pra
   vencer. */
html[data-viewport="mobile"] .mestrar-submodal.is-batalha {
    width: 100% !important;
    max-width: none !important;
    max-height: var(--mob-sheet-h) !important;
    padding: 12px 10px calc(12px + var(--mob-safe-b)) !important;
}

/* 0.229.0 — fundo do Local (style.css, `.is-batalha.tem-cena`) no celular.
   Aqui a arena e' bottom sheet RETRATO: 100vw x 92vh = ~0,54:1. A arte de Local e'
   paisagem — mediana 1,77:1, medida nas 13 artes de producao —, entao sob `cover` sobra
   so' ~30% da LARGURA. Duas correcoes, nenhuma delas cosmetica:
     - foco em 32% pega a linha do horizonte em vez do chao, que e' onde a silhueta do
       lugar (o que identifica o Local) costuma estar;
     - scrim mais forte porque o recorte vertical fica mais ampliado e mais claro, e a
       UI do celular ja e' mais densa.
   Sem `backdrop-filter` de proposito: e' proibido em tela cheia no celular por custo de
   GPU (ver o bloco do .mestrar-bn-end-bd mais abaixo).
   A faixa de topo (variacao 4 do estudo) resolveria melhor — mas e' outra variacao. */
html[data-viewport="mobile"] .mestrar-submodal.is-batalha.tem-cena {
    background:
        radial-gradient(125% 120% at 50% 42%, transparent 46%, rgba(0, 0, 0, 0.78) 100%),
        linear-gradient(180deg, rgba(16, 11, 7, 0.74) 0%, rgba(16, 11, 7, 0.9) 100%),
        var(--bn-cena-img, none) center 32% / cover no-repeat,
        radial-gradient(ellipse at 50% 30%, rgba(201, 168, 76, 0.12) 0%, rgba(201, 168, 76, 0) 60%),
        linear-gradient(160deg, #1a120a 0%, #100905 100%);
    background-origin: border-box;
}

html[data-viewport="mobile"] .mestrar-bn-layout {
    grid-template-columns: 1fr;
    gap: 10px;
}
/* A arena empilha: adversario em cima, VS no meio, jogador embaixo. */
html[data-viewport="mobile"] .mestrar-bn-arena {
    grid-template-columns: 1fr;
    gap: 8px;
    margin-bottom: 10px;
}
html[data-viewport="mobile"] .mestrar-bn-fighter {
    padding: 8px;
    gap: 6px;
}
/* O "VS" era uma coluna de grid de verdade, gastando ~42px de largura em
   decoracao. Empilhado, vira uma faixa fina. */
html[data-viewport="mobile"] .mestrar-bn-vs {
    font-size: 1rem;
    padding: 2px 0;
    text-align: center;
}
/* Combatente vira faixa horizontal: sprite a esquerda, nome/HP/MP a direita.
   Sem isso, dois cards empilhados de 150px de sprite comem a tela inteira.
   Ordem do DOM (battle_backend.js:851): nome, botao-da-imagem, HP, MP, vel. */
html[data-viewport="mobile"] .mestrar-bn-fighter-card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
        "img nome"
        "img hp"
        "img mp"
        "vel vel"
        "stsd stsd"
        "stsb stsb";
    align-items: center;
    gap: 4px 10px;
}
/* Buff/debuff no celular. No PC as duas colunas FLANQUEIAM o sprite (79px do
   centro do card, = metade dos 150px da imagem). Aqui isso nao funciona: o card
   virou faixa horizontal, o sprite encolheu pra min(28vw,110px) e vive na
   coluna da esquerda — colunas nos flancos ou cairiam fora do card ou cobririam
   nome e barras.
   Viram DUAS faixas horizontais no rodape do card, uma por natureza. A leitura
   "debuff a esquerda, buff a direita" sobrevive como ALINHAMENTO (os debuffs
   encostam na borda esquerda, os buffs na direita) em vez de posicao ao redor
   da imagem — e cada faixa so' existe se aquele lado tiver efeito, entao a
   linha some junto (a linha do grid e' `auto` e colapsa pra zero). */
html[data-viewport="mobile"] .mestrar-bn-status-col {
    position: static;
    transform: none;
    width: auto;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 4px;
}
html[data-viewport="mobile"] .mestrar-bn-status-col.is-debuff { grid-area: stsd; justify-content: flex-start; }
html[data-viewport="mobile"] .mestrar-bn-status-col.is-buff   { grid-area: stsb; justify-content: flex-end; }
/* Alvo de toque de verdade: o selo do PC tem 16px de altura, que e' metade do
   minimo tocavel. */
html[data-viewport="mobile"] .mestrar-bn-status-selo {
    font-size: 0.68rem;
    padding: 4px 7px;
    min-height: 26px;
}
html[data-viewport="mobile"] .mestrar-bn-fighter-card > .mestrar-bn-fighter-name    { grid-area: nome; text-align: left; justify-content: flex-start; font-size: 0.95rem; }
html[data-viewport="mobile"] .mestrar-bn-fighter-card > .mestrar-bn-fighter-img-btn { grid-area: img; }
html[data-viewport="mobile"] .mestrar-bn-fighter-card > .mestrar-bn-bar-hp          { grid-area: hp; }
html[data-viewport="mobile"] .mestrar-bn-fighter-card > .mestrar-bn-bar-mp          { grid-area: mp; }
html[data-viewport="mobile"] .mestrar-bn-fighter-card > .mestrar-bn-vel-grid        { grid-area: vel; }
html[data-viewport="mobile"] .mestrar-bn-fighter-card > .mestrar-bn-fighter-empty   { grid-area: nome; }
/* min() preserva 150px em qualquer viewport acima de ~395px — mas como esta
   regra so' existe no celular, o PC nao a ve' de qualquer forma.
   aspect-ratio fica: o FLIP da intro (battleIntroEmbed.tsx:143) mede este
   elemento, e com height:auto a medida antes da imagem carregar pode ser 0. */
html[data-viewport="mobile"] .mestrar-bn-fighter-img {
    width: min(28vw, 110px);
    height: min(28vw, 110px);
    aspect-ratio: 1;
}
/* Faixa do golpe (o nome da habilidade) no celular. No PC ela ocupa 46-88% de
   uma arena de duas colunas; aqui a arena empilha e fica estreita, entao a
   faixa precisa comecar mais larga e com fonte menor — senao "Impacto Da
   Carapaca Lunar" (o nome mais longo do banco, 25 caracteres) corta com
   ellipsis logo no primeiro uso, que e' justamente o que a faixa existe pra
   evitar. Sem backdrop-filter: e' proibido em tela cheia no celular por custo
   de GPU (ver o bloco do .mestrar-bn-end-bd mais abaixo). */
html[data-viewport="mobile"] .mestrar-bn-golpe-faixa {
    min-width: 84%;
    max-width: 96%;
    padding: 7px 14px;
    gap: 7px;
}
html[data-viewport="mobile"] .mestrar-bn-golpe-faixa-nome { font-size: 1.05rem; }
html[data-viewport="mobile"] .mestrar-bn-golpe-faixa-quem { font-size: 0.66rem; }
html[data-viewport="mobile"] .mestrar-bn-golpe-faixa-icone { width: 20px; height: 20px; }
/* A arena empilhada e' alta e o "top: 46%" cairia entre os dois lutadores, em
   cima do VS. Subir pro terco superior deixa a faixa sobre o adversario, que e'
   quem acabou de agir na maioria dos casos. */
html[data-viewport="mobile"] .mestrar-bn-golpe-faixa { top: 34%; }

/* Roster (trocar monstro) vira faixa rolavel, com alvo de toque de verdade. */
html[data-viewport="mobile"] .mestrar-bn-roster {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    justify-content: flex-start;
    scrollbar-width: none;
}
html[data-viewport="mobile"] .mestrar-bn-roster::-webkit-scrollbar { display: none; }
html[data-viewport="mobile"] .mestrar-bn-roster-item { flex: 0 0 auto; }

/* Paleta de habilidades: 5 por linha, 1 linha por pagina (pedido do dono).
   A paginacao em si e' JS — `battle_backend.js` (BN_SKILLS_PER_PAGE) e
   `mestrar_session.js` (BN_HAB_PER_PAGE) fixam 5 em TODO viewport desde a
   0.226.0, com Defender e Ataque Comum ocupando 2 vagas da pagina 1. Aqui so'
   cabe a geometria.

   A CONTA, sem maquiagem: o painel de acoes tem 318px uteis; com 5 colunas e
   gap 4 sobram (318-16)/5 = 60,4px por card. O `.hab-card` de desktop precisa
   de ~104px so' pra caber icone de tipo (22px) + reserva do botao 'i' (30px) +
   padding (24px). Em 60px ele NAO cabe de jeito nenhum — por isso as regras
   abaixo transformam o card num BOTAO DE ACAO (icone + nome), e nao num card
   de bestiario encolhido. Poder/CW/Custo e efetividade migram pro modal de
   detalhe, que o 'i' continua abrindo.

   Se ficar apertado demais no aparelho do dono, a troca e' de UM numero:
   `repeat(5, ...)` -> `repeat(3, ...)` aqui, e o pageSize no JS. */
html[data-viewport="mobile"] .mestrar-bn-skills {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 4px;
}
/* --- o card de habilidade vira botao de acao, SO' dentro da batalha --------
   Escopado em `.mestrar-bn-skills` de proposito: o mesmo `.hab-card` aparece
   no bestiario, no Painel do Mestre e na ficha, e la' ele deve continuar
   inteiro. */
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card {
    /* os 28px de padding-bottom do desktop reservam espaco pro olhinho do
       editor, que na batalha nunca e' renderizado — sao 28px mortos. */
    padding: 6px 3px 6px;
    min-width: 0;
}
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-nome-row {
    /* 30px de padding-right reservavam o botao 'i' ao lado do nome; aqui ele
       vira canto absoluto, entao a reserva some. */
    padding-right: 0;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    text-align: center;
}
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-tipo-icon {
    width: 26px;
    height: 26px;
    flex: 0 0 auto;
}
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-nome {
    font-size: 0.6rem;
    line-height: 1.15;
    white-space: normal;
    overflow-wrap: anywhere;
}
/* Fora do botao de acao: informacao que so' faz sentido lendo com calma, e que
   o 'i' abre no modal de detalhe. */
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-stats,
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-efetividade,
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-meta {
    display: none;
}
/* O 'i' e' a UNICA forma de ler a habilidade com o nome a 0.6rem — nao pode
   sumir. Vai pro canto, com area de toque maior que o desenho via inset
   negativo do ::after. */
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-detail {
    position: absolute;
    top: 1px;
    right: 1px;
    width: 18px;
    height: 18px;
    padding: 0;
    z-index: 2;
}
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-detail::after {
    content: '';
    position: absolute;
    inset: -9px;
}
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-detail svg {
    width: 14px;
    height: 14px;
}
/* A badge de cooldown fica: e' informacao acionavel do turno. */
html[data-viewport="mobile"] .mestrar-bn-skills .hab-card-cw-badge {
    font-size: 0.55rem;
    padding: 1px 3px;
}
/* A barra de paginacao dentro do painel de acoes precisa ser rasa — cada px
   aqui e' px que a arena perde. */
html[data-viewport="mobile"] .mestrar-bn-action-panel .bestiary-pagination {
    margin-top: 6px;
    padding: 2px 0;
    gap: 6px;
}
/* Log: 220px fixos de altura numa tela onde a conta ja' nao fecha. Colapsa
   em 2 linhas; o header dele ja' e' uma barra clicavel. */
html[data-viewport="mobile"] .mestrar-bn-log-side {
    /* `min-height: 0` e' obrigatorio: style.css:26845 (dentro de
       @media max-width:1000px, ou seja TODO celular) declara
       `min-height:160px`, e pelas regras de dimensionamento do CSS o
       min-height vence o max-height. Sem esta linha o log continuaria com
       160px e roubaria 40px da arena — exatamente o oposto da intencao. */
    min-height: 0;
    max-height: 120px;
}
html[data-viewport="mobile"] .mestrar-bn-log-list {
    max-height: 76px;
    overflow-y: auto;
    overscroll-behavior: contain;
}
/* Painel de acoes fixo no rodape do modal rolante. */
html[data-viewport="mobile"] .mestrar-bn-action-panel {
    position: sticky;
    bottom: 0;
    z-index: 2;
    padding: 10px;
    background: linear-gradient(180deg, rgba(16, 9, 5, 0.94), rgba(16, 9, 5, 0.99));
}
/* Linha "Forcar" do narrador: campos de 160px e selects de 130px viravam uma
   linha cada. */
html[data-viewport="mobile"] .mestrar-bn-input {
    width: 100%;
    min-width: 0;
}
html[data-viewport="mobile"] .mestrar-bn-select-narrow {
    min-width: 0;
    flex: 1 1 auto;
}
/* Detalhe do combatente (abre ao tocar no sprite): grid de 180px + 170px
   dentro de 305px, sem nenhuma media query. */
html[data-viewport="mobile"] .mestrar-bn-detail-grid {
    grid-template-columns: 1fr;
}
html[data-viewport="mobile"] .mestrar-bn-detail-img-wrap {
    width: min(40vw, 160px);
    height: min(40vw, 160px);
    margin: 0 auto;
}
html[data-viewport="mobile"] .mestrar-bn-detail-habs {
    grid-template-columns: 1fr;
}
html[data-viewport="mobile"] .mestrar-bn-detail-stats {
    grid-template-columns: 1fr;
}
/* Titulo de vitoria com nowrap + tracking de 0.18em nao cabe e elipsa. */
html[data-viewport="mobile"] .mestrar-end-title {
    white-space: normal;
    letter-spacing: 0.08em;
    font-size: 1.35rem;
}

/* Cockpit da sessao ao vivo (narrador). */
html[data-viewport="mobile"] .mestrar-cockpit {
    height: 100dvh;
    max-height: 100dvh;
}


/* ==========================================================================
 * 14. TOAST
 *
 * `left:50%` sem `width`/`max-width`/`right`: pra um fixed assim o teto do
 * shrink-to-fit e' (viewport - left) = 180px numa tela de 360. Mensagem longa
 * vira uma coluna de 6 linhas. No desktop o teto e' 960px e o bug some.
 * ========================================================================== */
html[data-viewport="mobile"] .toast-notification {
    width: max-content;
    max-width: min(92vw, 520px);
    bottom: calc(var(--mob-nav-h) + var(--mob-safe-b) + 12px);
    text-align: center;
}


/* ==========================================================================
 * 15. NARRADOR — telas densas
 * ========================================================================== */
html[data-viewport="mobile"] .mestre-painel {
    height: 100dvh;
    max-height: 100dvh;
}
html[data-viewport="mobile"] .mestre-npc-monster-row {
    grid-template-columns: 1fr;
    gap: 4px;
}
html[data-viewport="mobile"] .hab-form-layout,
html[data-viewport="mobile"] .mestre-form-row {
    grid-template-columns: 1fr;
}
/* Os dois grids do viewer de NPC nao tem CLASSE: sao <div style="display:grid;
   grid-template-columns:260px 1fr;..."> escritos direto no template literal
   (mestre_painel.js:10227 e :10502). Sem isto a coluna de conteudo fica com
   54px e 96px respectivamente.

   Como o layout mora em estilo INLINE, nenhum seletor normal alcanca — so'
   !important. E o gancho tem que ser o proprio texto do atributo.

   LIMITACAO ACEITA CONSCIENTEMENTE: se alguem trocar o `260px`/`220px` no JS,
   estas duas regras deixam de casar. A falha e' benigna (volta ao estado de
   hoje, nao quebra nada) e o verificador nao pega. O conserto definitivo e'
   dar uma classe aos dois divs — mas isso e' editar um arquivo de 935KB que
   outra sessao mexe, entao fica pra quando o dono aprovar. */
html[data-viewport="mobile"] .mestre-form-body > [style*="grid-template-columns:260px 1fr"],
html[data-viewport="mobile"] .mestre-form-body > [style*="grid-template-columns:220px 1fr"] {
    grid-template-columns: 1fr !important;
}
/* .exp-dias-grid tem 7 colunas -> ~43px por botao, abaixo do alvo. */
html[data-viewport="mobile"] .exp-dias-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* Aviso de tela so'-desktop — inserido pelo mobile_shell.js no editor de
   mapa, cujo bloqueio nao e' layout: o fantasma de posicionamento nasce num
   `mousemove` (editor.js:8448), evento que nao existe no toque. */
html[data-viewport="mobile"] .mob-desktop-only {
    margin: 12px;
    padding: 14px 16px;
    border: 1px solid rgba(201, 168, 76, 0.45);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.5);
    color: var(--parchment, #e8d8b8);
    font-family: 'Lora', serif;
    font-size: 0.9rem;
    line-height: 1.5;
    text-align: center;
}


/* ==========================================================================
 * 16. PERFORMANCE — o que e' caro demais num ARM medio
 * ========================================================================== */

/* backdrop-filter em tela cheia e' das operacoes mais caras em GPU movel, e
   estes dois disparam junto com a animacao da barra de XP. */
html[data-viewport="mobile"] .mestrar-bn-end-bd,
html[data-viewport="mobile"] .mestrar-end-loading-bd {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: rgba(0, 0, 0, 0.82);
}
/* card_anim.js liga uma animacao INFINITA + will-change em cada card
   (`respira` e' o default) e um segundo pseudo-elemento animado por cima. Com
   N cards na tela sao N camadas de composicao permanentes. E o modo `holo` e'
   dirigido por ponteiro — inutil no toque. */
html[data-pointer="coarse"] body.cardanim-respira img.bestiary-card-img {
    animation: none !important;
    will-change: auto !important;
}
/* A "sombra que respira" (card_anim.js:191) NAO e' desligada aqui de
   proposito: ela mora em `.bestiary-card-img-wrap::after`, o MESMO
   pseudo-elemento do foil dourado do shiny (style.css:4134). Um
   `animation:none !important` ali congelaria o brilho de todo card shiny —
   trocar um ganho de GPU por um bug visual em card raro nao compensa. */


/* ==========================================================================
 * 17. PAISAGEM — suportada, nao otimizada
 *
 * Bloquear e' hostil (quebra suporte de mesa e e' problema de acessibilidade),
 * e o mapa fica melhor deitado. So' encolhemos o cromo pra sobrar altura.
 * ========================================================================== */
@media (orientation: landscape) and (max-height: 500px) {
    html[data-viewport="mobile"] #top-bar {
        padding-top: calc(2px + var(--mob-safe-t));
        padding-bottom: 2px;
    }
    html[data-viewport="mobile"] #nav-status {
        flex: 0 1 auto;
    }
    /* A variavel tem que ser redefinida no <html>, NAO na .mob-nav: quem a
       consome sao IRMAOS dela (#map no `bottom`, .leaflet-top, o toast), e
       custom property so' herda pra descendente. Declarada na barra, o mapa
       continuaria reservando 56px pra uma barra de 44px — 12px de faixa
       morta bem na altura do polegar. */
    html[data-viewport="mobile"] {
        --mob-nav-h: 44px;
    }
    html[data-viewport="mobile"] .mob-nav .nav-btn {
        min-height: 44px;
    }
    html[data-viewport="mobile"] .char-modal,
    html[data-viewport="mobile"] .mestre-form-modal,
    html[data-viewport="mobile"] .mestrar-submodal,
    html[data-viewport="mobile"] .alternart-modal {
        max-height: 100dvh;
        border-radius: 0;
    }
}


/* ==========================================================================
 * RAID — camada de celular (spec-raid) · 2026-08-12
 *
 * Par do bloco "RAID" no fim do style.css. Todo seletor daqui comeca com
 * `html[data-viewport="mobile"]`, entao no PC este trecho e' INERTE.
 *
 * O que ja vem de graca e NAO se repete aqui:
 *   - a casca: `.raid-tela` e' um `.mestrar-submodal` e `.raid-painel-modal` e'
 *     um `.mestre-form-modal` — a secao 8 ja transforma os dois em bottom sheet
 *     (100% de largura, `--mob-sheet-h` de altura, `--mob-safe-b` no rodape).
 *     Como as regras de la' sao (0,2,1) e as minhas do style.css sao (0,1,0),
 *     elas vencem sozinhas: nenhum !important e' necessario.
 *   - a faixa de habilidades: e' a MESMA `.mestrar-bn-skills` da batalha, ja
 *     tratada na secao 15 (5 colunas + `.hab-card` virando botao de acao). NAO
 *     mexemos no numero de colunas: 5 e' PAR com o pageSize do JS
 *     (`BN_SKILLS_PER_PAGE = 5` no raid.js, igual ao battle_backend.js), e uma
 *     pagina tem que caber em UMA linha. Trocar pra 2 colunas aqui daria 3
 *     linhas por pagina, que e' o defeito que aquela regra veio consertar.
 *   - o empilhamento do grid de baixo e do card de raid: o style.css ja resolve
 *     nos @media de 980px/760px, que o celular tambem ve'.
 *
 * O que sobra pro celular e' o que so' existe aqui: sprite elastico, alvos de
 * toque de 44px, a gaveta do log e o custo de GPU do blur.
 * ========================================================================== */

/* -------- arena: respiro menor, o conteudo e' que manda --------
   O rodape seguro ja vem da secao 8 (`padding-bottom` no proprio sheet), entao
   aqui NAO se soma `--mob-safe-b` de novo — seriam duas reservas empilhadas. */
html[data-viewport="mobile"] .raid-tela .v1-arena {
    padding: 10px 10px 0;
}
html[data-viewport="mobile"] .raid-tela .v1-baixo {
    padding: 10px 10px 8px;
    gap: 10px;
}
html[data-viewport="mobile"] .raid-topo {
    padding: 7px 10px 8px;
    gap: 6px;
}
html[data-viewport="mobile"] .raid-tit { font-size: 0.95rem; }

/* -------- sprite do boss: ELASTICO --------
   `min(vw, px)` e' a forma do arquivo: cresce com a tela ate um teto. Sem o teto,
   um tablet em modo celular renderiza um boss de 400px e o HUD sai da dobra;
   sem o vw, o aparelho estreito recebe um sprite que nao cabe.
   A caixa continua com `width: auto` (style.css), entao a sombra de contato e o
   halo, medidos em % dela, encolhem junto — nao viram uma poca de 300px debaixo
   de um bicho de 150px. */
html[data-viewport="mobile"] .raid-boss-arte {
    height: min(42vw, 208px);
}
html[data-viewport="mobile"] .raid-boss-palco {
    width: 100%;
    gap: 8px;
}
/* Blur PROIBIDO em tela cheia no celular (mesma razao do `.mestrar-bn-end-bd`
   mais acima: custo de GPU). O contraste que o blur dava volta pelo fundo, que
   fica mais opaco — o HUD nunca pode perder legibilidade sobre a foto. */
html[data-viewport="mobile"] .raid-boss-hud {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: linear-gradient(180deg, rgba(8, 5, 3, 0.86), rgba(8, 5, 3, 0.94));
    padding: 7px 10px 8px;
    gap: 8px;
}
html[data-viewport="mobile"] .raid-mon-card {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: linear-gradient(180deg, rgba(20, 14, 8, 0.94), rgba(9, 6, 4, 0.96));
}
/* A label de poder para de brigar por largura com o texto do HUD. */
html[data-viewport="mobile"] .raid-boss-hud .raid-poder-wrap { width: 100%; }
/* O painel do boss e' montado pelo componente unico do rodape (`opRodapeHtml`,
   battle_backend.js) e o `.raid-poder-wrap` passou a viver dentro da tampa
   `.mestrar-bn-op-poder`. Aqui no celular o `.raid-boss-hud` continua o flex em
   LINHA de sempre, e a tampa, sem base propria, encolhia ao conteudo e cabia AO
   LADO dos dados (medido: 322px de barra + a tampa na mesma linha). A linha
   inteira devolve o "poder embaixo" que este layout sempre teve. */
html[data-viewport="mobile"] .raid-boss-hud > .mestrar-bn-op-poder { flex: 1 1 100%; }
/* A vida e o contador "quebradas N de T" (M4) vem no mesmo no'. No desktop ficam
   lado a lado; aqui nao ha largura pra isso — o contador desce pra baixo da
   barra, encostado a direita, com um respiro. */
html[data-viewport="mobile"] .raid-boss-hud .mestrar-bn-op-vida {
    display: flex; flex-direction: column; align-items: flex-end; gap: 5px;
}
/* O sprite do jogador tambem encolhe — o card dele divide a tela com a faixa
   de habilidades, e 172px de altura comiam a dobra inteira. */
html[data-viewport="mobile"] .raid-sprite { height: min(34vw, 132px); }

/* -------- barras: a de vida engorda, o texto tambem --------
   No celular a barra e' o unico indicador que o polegar nao cobre. O numero
   escrito dentro dela e' obrigatorio (e' o que sobra pra quem desliga animacao),
   entao ele nao pode encolher junto com a tela. */
html[data-viewport="mobile"] .raid-barra-emp.alta { height: 30px; }
html[data-viewport="mobile"] .raid-barra-emp > b.lab { font-size: 0.6rem; gap: 6px; }
html[data-viewport="mobile"] .raid-vida-num { font-size: 0.58rem; }

/* -------- utilitarios e CTAs: alvo de toque de verdade -------- */
html[data-pointer="coarse"] .raid-util,
html[data-pointer="coarse"] .raid-dia-btn,
html[data-pointer="coarse"] .raid-drawer-btn {
    min-height: 44px;
}
html[data-pointer="coarse"] .raid-chip-x {
    width: 26px; height: 26px;
}
html[data-viewport="mobile"] .raid-utils .raid-util { flex: 1 1 30%; text-align: center; }

/* -------- GAVETA DO LOG: fechada por padrao, e no celular ela cobre --------
   O `transform: translateX(101%)` do style.css ja nasce fechado (o raid.js so'
   poe `log-aberto` quando o jogador pede), entao aqui nao ha nada a "fechar" —
   o que muda e' a largura: numa tela de 360px uma gaveta de 300px que deixa uma
   fresta de 60px nao mostra nem o log nem a arena. Ela vira folha quase cheia,
   e o botao continua sendo a unica rota de volta (ele fica POR CIMA, z-index
   maior que a gaveta, senao a gaveta aberta engole o proprio botao de fechar). */
html[data-viewport="mobile"] .raid-drawer {
    width: min(92%, 340px);
    padding: 10px;
}
html[data-viewport="mobile"] .raid-drawer-btn {
    z-index: 9;
    padding: 16px 6px;
}
html[data-viewport="mobile"] .raid-log li { font-size: 0.74rem; }

/* -------- painel de selecao -------- */
html[data-viewport="mobile"] .raid-card {
    grid-template-columns: 64px minmax(0, 1fr);
    gap: 9px; padding: 9px 10px;
}
html[data-viewport="mobile"] .raid-card-arte { width: 64px; height: 64px; }
html[data-viewport="mobile"] .raid-card-nome { font-size: 0.9rem; }
html[data-viewport="mobile"] .raid-card-desc { font-size: 0.72rem; }
/* A CTA ocupa a linha inteira embaixo (o @media de 980px do style.css ja faz o
   `grid-column`); aqui ela vira botao cheio, do jeito que o polegar espera. */
html[data-viewport="mobile"] .raid-card-cta .raid-util { flex: 1 1 100%; }
html[data-viewport="mobile"] .raid-barra-seg { height: 14px; }

/* -------- aba do narrador -------- */
html[data-viewport="mobile"] .raid-bloco { padding: 9px 10px; margin: 8px 0; }
html[data-viewport="mobile"] .raid-grid-2,
html[data-viewport="mobile"] .raid-grid-par,
html[data-viewport="mobile"] .raid-prem-body {
    grid-template-columns: minmax(0, 1fr);
}
/* Rotulo em cima do valor: com 96px de coluna de chave sobra menos de metade da
   tela pro valor, e todo resumo vira uma coluna de duas palavras. */
html[data-viewport="mobile"] .raid-rev-linha {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
}
html[data-viewport="mobile"] .raid-dias-grid { gap: 5px; }
html[data-viewport="mobile"] .raid-dia-btn { flex: 1 1 60px; min-width: 0; }
html[data-viewport="mobile"] .raid-dec-btns { flex-direction: column; }
html[data-viewport="mobile"] .raid-dec-btn { flex: 1 1 auto; width: 100%; }
/* A lista de areas rola dentro do sheet; sem teto proprio ela empurraria os
   botoes de acao pra fora da folha. */
html[data-viewport="mobile"] .raid-area-lista { max-height: 52vh; }
html[data-pointer="coarse"] .raid-area-item { padding: 10px 9px; }
html[data-viewport="mobile"] .raid-fundo-prev { max-height: 150px; }
html[data-viewport="mobile"] .raid-fila-head { gap: 8px; }

/* --------------------------------------------------------------------------
   Dialogo de TROCA de item de tipagem (.md-troca — style.css).
   No PC o par "sai / entra" e' lado a lado; no celular os dois cards ficariam
   com ~90px cada e o nome do item quebraria letra a letra. Vira coluna, e a
   seta deita junto pra continuar apontando o sentido da troca.
   -------------------------------------------------------------------------- */
html[data-viewport="mobile"] .md-troca-par {
    flex-direction: column;
    gap: 8px;
}
html[data-viewport="mobile"] .md-troca-seta {
    justify-content: center;
    transform: rotate(90deg);
}
