/*
 * Letmaze — mecânica exclusiva (labirinto + tokens animados). A
 * casca genérica (topbar, badges, modais, stats, log) vem de
 * ../shared/base.css; cores/tipografia vêm do tema terra
 * (../shared/styles/theme-terra.css, fixo — não há "dicionário" cujo tema
 * trocar, ver index.php).
 */

/* .layout (shared/base.css) é flex por padrão — bom o bastante como está.
   FASE/PONTUAÇÃO/MOVIMENTOS vivem dentro de .side-panel, junto com o resto
   dos stats (tempo, vidas, log); em telas estreitas o media query no fim
   deste arquivo usa o mesmo truque de grid nomeado + display:contents do
   Letrix/Letfall pra puxar só os cards certos pra cima do tabuleiro sem
   duplicar HTML (ver comentário lá embaixo). */
.board-column{
  /* flex-basis dá uma largura de partida — sem isso, antes da grade ter
     células o board-column colapsa pra largura de conteúdo zero (mesmo
     motivo do Letrix, ver letrix.css). */
  flex: 1 1 400px;
  min-width: 280px;
}

/* side em max-width:200px — ~10% mais estreito que a faixa 200-220px (que
   já era referenciada no .side-panel do Letrix, ver base.css: flex:1/
   min-width:180px + min-width:200px no letrix.css) por pedido explícito,
   pra sobrar ainda mais coluna pro board-column. Era minmax(220px, 300px)
   originalmente — o teto de 300px deixava o painel mais largo que o do
   Letrix e roubava espaço horizontal do tabuleiro (.maze-cell não tem
   limite de altura, só largura-do-tabuleiro/cols — quanto mais coluna
   sobra pro board, maior a célula em fases avançadas, ver config.php ->
   maze.max_size). flex-grow:0 trava nesse teto em vez de crescer junto
   com o board-column. */
.side-panel{
  flex: 0 1 200px;
  min-width: 180px;
}

.board-wrap{
  /* Mesmo truque do Letfall/Letrix: z-index próprio evita que .stat-accent
     (barra ::before) do side-panel pinte por cima de qualquer coisa que
     escape da caixa do board-wrap. min-height evita colapso quase-zero
     antes do primeiro "Jogar" (senão #start-modal, position:absolute
     dentro dele, fica confinado a uma altura quase-zero). */
  position: relative;
  z-index: 1;
  min-height: 320px;
  padding: 14px;
}

.board-wrap.has-grid{
  background: var(--panel-texture, none), var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: var(--card-shadow, 0 2px 6px rgba(0,0,0,.3));
}

/* .modal (base.css) não define z-index próprio (fica em "auto"). Os tokens
   (.token-sheep/.token-wolf, logo abaixo) têm z-index POSITIVO explícito
   pra ovelha ficar por cima dos lobos — mas como .board-wrap já é a
   stacking context mais próxima (position:relative + z-index:1, também
   logo acima), um z-index positivo qualquer dentro dela pinta por cima de
   QUALQUER elemento com z-index:auto da mesma context, modal incluído,
   não importa a ordem no HTML. Sem isto, os tokens ficavam visíveis por
   cima do card de fim de fase/derrota. Um valor bem acima do maior
   z-index de token (2) resolve pros três modais que podem abrir com o
   labirinto ainda montado por trás (start/win/give-up/field-change/howto).
*/
.modal{
  z-index: 5;
}

/* #howto-modal (shared/base.css) traz seu próprio z-index:3 num seletor
   por ID — que, por especificidade CSS, VENCE o `.modal{z-index:5}`
   acima (ID sempre bate classe, independente da ordem dos arquivos).
   Isso deixaria #howto-modal com z-index MENOR que start/win/give-up/
   field-change (5), abrindo atrás deles — diferente do Letrix/Letchess,
   onde `.modal` base fica em z-index:auto e o z-index:3 já é suficiente.
   Especificidade igual (ID vs ID) decide por ordem de arquivo — como este
   arquivo carrega DEPOIS de shared/base.css, esta regra vence e bate o 5
   de cima. #stats-modal tem o mesmo problema, mas é resolvido direto em
   public/css/stats-panel.css (que carrega DEPOIS deste arquivo — um
   z-index maior aqui seria vencido por aquele, na ordem inversa). */
#howto-modal{
  z-index: 6;
}

/* Nem #start-modal nem #win-modal têm algo "escondido" pra proteger do
   scrim escuro padrão de .modal (base.css): #start-modal é a primeira tela
   (nada atrás ainda); #win-modal cobre um labirinto que ou foi resolvido,
   ou já teve o caminho revelado (Desistir/game over) — está tudo às claras
   no momento em que o modal abre. Mesmo raciocínio do Letrix. */
#start-modal{
  background: transparent;
  align-items: flex-start;
}
#win-modal{
  background: transparent;
}

/* Card arrastável (ver makeCardDraggable() em game.js) — cursor sinaliza
   que dá pra mover pra ver o labirinto por trás; touch-action:none evita
   que o navegador tente rolar a página no lugar de arrastar no toque. */
#win-card{
  cursor: grab;
  touch-action: none;
}
#win-card.dragging{
  cursor: grabbing;
  user-select: none;
}

.maze-grid{
  position: relative;
  display: grid;
  gap: 0;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

/* Botões direcionais abaixo do tabuleiro (D-pad) — mesmo estilo/markup do
   Letfall (.mobile-controls/.ctrl-btn em letfall.css), copiado aqui pra
   manter a identidade visual entre os jogos. Sempre visíveis (não só em
   telas de toque): em fases avançadas o labirinto fica com células bem
   pequenas, e mirar o toque numa célula certa fica difícil mesmo com
   mouse — os botões dão um alvo grande e fixo pra manobrar a ovelha,
   complementando (não substituindo) o toque direto no labirinto
   (computeStraightWalk() em game.js) e o teclado. */
.mobile-controls{
  display:flex;
  gap:8px;
  margin-top:10px;
}
/* Mesma especificidade do .hidden genérico (shared/base.css) — sem este
   override de duas classes, .mobile-controls{display:flex} venceria por
   vir DEPOIS na cascata (letmaze.css carrega depois de base.css, ver
   index.php), e o botão ficaria visível mesmo com a classe "hidden"
   aplicada. Mesmo problema que .modal já tinha (ver .modal.hidden em
   base.css). */
.mobile-controls.hidden{ display:none; }
.ctrl-btn{
  flex:1;
  aspect-ratio: 1 / 1;
  min-height:48px;
  display:flex;
  align-items:center;
  justify-content:center;
  background: linear-gradient(180deg, rgba(255,255,255,calc(.16 * var(--effect-3d, 1))) 0%, rgba(255,255,255,0) 45%, rgba(0,0,0,calc(.14 * var(--effect-3d, 1))) 100%), var(--bg);
  color:var(--text);
  border:1px solid var(--border);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,calc(.22 * var(--effect-3d, 1))),
    inset 0 -2px 1px rgba(0,0,0,calc(.16 * var(--effect-3d, 1))),
    var(--badge-shadow, 0 1px 2px rgba(0,0,0,.12));
  border-radius: var(--tile-radius, 6px);
  cursor:pointer;
  touch-action:manipulation;
  -webkit-tap-highlight-color:transparent;
  user-select:none;
  transition: transform .1s ease, background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.ctrl-btn svg{ display:block; }
.ctrl-btn:hover{
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,calc(.28 * var(--effect-3d, 1))),
    inset 0 -2px 1px rgba(0,0,0,calc(.14 * var(--effect-3d, 1))),
    var(--card-shadow-hover, 0 3px 8px rgba(0,0,0,.16));
}
.ctrl-btn:active{
  background: linear-gradient(180deg, rgba(0,0,0,calc(.22 * var(--effect-3d, 1))) 0%, rgba(0,0,0,0) 50%, rgba(255,255,255,calc(.1 * var(--effect-3d, 1))) 100%), var(--red-ink);
  border-color:var(--red-ink);
  transform:scale(0.93);
  box-shadow:
    inset 0 2px 3px rgba(0,0,0,calc(.35 * var(--effect-3d, 1))),
    0 1px 1px rgba(0,0,0,calc(.15 * var(--effect-3d, 1)));
}

/* Cima/baixo: peça única de largura dupla (2 slots) cortada na diagonal —
   "cima" ocupa o triângulo superior-esquerdo, "baixo" o inferior-direito
   (pedido explícito: no aperto de duas mãos, o polegar esquerdo alcança
   mais fácil a metade esquerda da barra, o direito a metade direita — cada
   botão fica do lado do polegar que aciona ele com mais naturalidade). Uma
   fileira reta de 4 botões deixava cima/baixo lado a lado, sem relação
   espacial nenhuma com "pra cima"/"pra baixo" no tabuleiro; a diagonal
   resolve isso sem esticar a barra pra 3 linhas (ver .ctrl-tri-up logo
   abaixo pro drop-shadow que faz os dois triângulos parecerem uma peça só
   com uma leve dobra na costura — só visual, a área clicável de cada um
   continua exatamente a metade). */
.ctrl-updown{
  flex: 2;
  position: relative;
  aspect-ratio: 2 / 1;
  min-height: 48px;
  border-radius: var(--tile-radius, 6px);
  overflow: hidden;
  border: 1px solid var(--border);
  box-shadow: var(--badge-shadow, 0 1px 2px rgba(0,0,0,.12));
  background: var(--bg);
}
.ctrl-tri{
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255,255,255,calc(.16 * var(--effect-3d, 1))) 0%, rgba(255,255,255,0) 45%, rgba(0,0,0,calc(.14 * var(--effect-3d, 1))) 100%), var(--bg);
  color: var(--text);
  border: 0;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  /* Mesmo bisel do .ctrl-btn (linha clara em cima, sombra escura embaixo)
     — faltava aqui no estado BASE (só existia em :hover, ver abaixo), por
     isso a linha clara sumia no modo noturno comparado a esquerda/direita.
     box-shadow é recortado pelo clip-path junto com o resto da caixa: como
     só o triângulo de CIMA toca a borda superior inteira (o de BAIXO só
     toca aquele canto), a linha clara aparece cheia só em cima — e o
     inverso pra sombra escura embaixo, que só aparece cheia no de baixo.
     Isso é o esperado (seguindo a borda física de cada triângulo), não um
     bug. */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,calc(.22 * var(--effect-3d, 1))),
    inset 0 -2px 1px rgba(0,0,0,calc(.16 * var(--effect-3d, 1))),
    var(--badge-shadow, 0 1px 2px rgba(0,0,0,.12));
  transition: transform .1s ease, background-color .15s ease, box-shadow .15s ease;
}
/* Mesmo hover/active do .ctrl-btn (esquerda/direita) — só sem border-color
   (border:0 aqui, o contorno de verdade é o do .ctrl-updown por fora) e sem
   scale() no :active (encolher em torno do centro da CAIXA CHEIA, não do
   triângulo recortado pelo clip-path, puxaria a peça pra fora da diagonal
   compartilhada com a outra metade — pareceria descolar da costura). O
   translateY(-1px) do :hover fica: só 1px, o mesmo "descolamento" já existe
   nos outros botões (mesma sensação tátil) — a linha de costura (svg
   separado, não afetado pelo transform) fica 1px "atrás" enquanto o mouse
   segura em cima, imperceptível e reverte ao sair. Sem essa translateY, a
   diferença de cor sozinha (box-shadow) ficava fraca demais no modo
   noturno pra registrar como "hover" de verdade. */
.ctrl-tri:hover{
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,calc(.28 * var(--effect-3d, 1))),
    inset 0 -2px 1px rgba(0,0,0,calc(.14 * var(--effect-3d, 1))),
    var(--card-shadow-hover, 0 3px 8px rgba(0,0,0,.16));
}
/* Ícone no CENTRÓIDE do triângulo (média dos 3 vértices), não no centro da
   caixa cheia (que cairia bem em cima da diagonal, ver clip-path abaixo) —
   pra "cima" isso é 1/3, 1/3; pra "baixo", o espelho, 2/3, 2/3. Posicionado
   via left/top+translate em vez do flex align/justify do .ctrl-tri (que só
   centraliza dentro da CAIXA cheia, não do triângulo recortado pelo
   clip-path). */
.ctrl-tri svg{
  display: block;
  position: absolute;
  transform: translate(-50%, -50%);
}
/* Corte exato na diagonal (sem fresta) — a "costura" visível é desenhada
   à parte por #ctrl-updown-seam (linha de <svg>, logo abaixo), não por um
   gap no clip-path: um gap em px teria largura VISUAL desigual num
   retângulo 2:1 (a diagonal real não é 45°, então um recuo igual em x e em
   y não gera uma faixa de espessura constante). A área clicável de cada
   botão continua sendo exatamente a metade — a linha por cima é só
   decorativa (pointer-events:none). */
.ctrl-tri-down{
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
}
.ctrl-tri-down svg{ left: 66.67%; top: 66.67%; }
.ctrl-tri-up{
  clip-path: polygon(0 0, 100% 0, 0 100%);
}
.ctrl-tri-up svg{ left: 33.33%; top: 33.33%; }
/* viewBox 100x50 casa com aspect-ratio:2/1 do .ctrl-updown acima — se essa
   proporção mudar, o viewBox precisa mudar junto (senão a linha não bate
   mais exatamente nos dois cantos opostos). vector-effect:non-scaling-stroke
   cancela o esticamento não-uniforme do viewBox->caixa (preserveAspectRatio
   ="none"): sem isso, stroke-width:1 renderizaria mais grosso na direção
   esticada, e não bateria com a largura de 1px de var(--border) usada no
   contorno dos outros botões (ver .ctrl-btn). */
.ctrl-updown-seam{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  stroke: var(--border);
  stroke-width: 1;
}
.ctrl-tri:active{
  background: linear-gradient(180deg, rgba(0,0,0,calc(.22 * var(--effect-3d, 1))) 0%, rgba(0,0,0,0) 50%, rgba(255,255,255,calc(.1 * var(--effect-3d, 1))) 100%), var(--red-ink);
  box-shadow:
    inset 0 2px 3px rgba(0,0,0,calc(.35 * var(--effect-3d, 1))),
    0 1px 1px rgba(0,0,0,calc(.15 * var(--effect-3d, 1)));
}

.board-wrap.has-grid .maze-grid{
  background: var(--canvas-bg);
  border: 2px solid var(--border);
  border-radius: var(--tile-radius);
}

.maze-cell{
  aspect-ratio: 1;
  box-sizing: border-box;
  border: 0 solid var(--border);
  cursor: pointer;
}

/* Toque/clique numa célula fora de uma reta válida a partir da ovelha, ou
   numa reta já bloqueada por parede logo na primeira célula (ver
   computeStraightWalk() em game.js) — pisca em vermelho e não muda nada,
   mesmo espírito do .ws-cell.wrong do Letrix. */
.maze-cell.tap-invalid{
  animation: letmaze-tap-invalid .26s ease;
}
@keyframes letmaze-tap-invalid{
  0%, 100%{ background: transparent; }
  50%{ background: rgba(193,87,61,.35); }
}

/* Célula de chegada: só o ícone de cercado marca ela, sem tom de fundo —
   a figura (::after, SVG embutido via data URI — mesmo truque já usado em
   shared/base.css pra seta do <select>) tem que ficar com fundo transparente,
   não um quadrado colorido atrás dela. Não é um token gerenciado por JS: ele
   nunca se move, diferente da ovelha/lobos. Antes era o emoji de bandeira
   (🏁); trocado pra combinar com o texto "chegou ao cercado" do modal de
   vitória (ver i18n) — nenhum dos dois fala mais em bandeira. */
.maze-cell.cell-end{
  position: relative;
}
.maze-cell.cell-end::after{
  content: "";
  position: absolute;
  inset: 16%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect x='10' y='20' width='13' height='66' rx='3' fill='%238B6142'/%3E%3Crect x='43.5' y='15' width='13' height='71' rx='3' fill='%238B6142'/%3E%3Crect x='77' y='20' width='13' height='66' rx='3' fill='%238B6142'/%3E%3Crect x='5' y='32' width='90' height='12' rx='3' fill='%236B4A30'/%3E%3Crect x='5' y='60' width='90' height='12' rx='3' fill='%236B4A30'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  pointer-events: none;
}

/* Item de vida extra ("touça de capim" — a cada N fases, ver config.php ->
   extra_life_item), sempre fora do caminho-solução. Mesmo padrão do
   .cell-end: ícone só decorativo via ::after, nunca um token gerenciado
   por JS como ovelha/lobo — só sua PRESENÇA/AUSÊNCIA muda (removeItemMarker()
   em game.js tira a classe quando a ovelha pega). Emoji de erva (🌿) já é
   reconhecível o bastante, sem precisar de outro ícone SVG desenhado à
   mão. Pulsação sutil chama atenção — está fora da rota direta, fácil de
   passar batido sem destaque nenhum. */
.maze-cell.cell-item{
  position: relative;
}
.maze-cell.cell-item::after{
  content: "🌿";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 120%;
  line-height: 1;
  pointer-events: none;
  animation: letmaze-item-pulse 1.6s ease-in-out infinite;
}
@keyframes letmaze-item-pulse{
  0%, 100%{ transform: scale(1); opacity: .85; }
  50%{ transform: scale(1.15); opacity: 1; }
}

/* As 4 classes de parede são combináveis por célula (uma célula pode ter
   parede nos 4 lados). Só N/W são desenhados por TODA célula; S/E só pelas
   células da última linha/coluna — cada parede interna já é a parede N (ou
   W) da célula vizinha, desenhar dos dois lados dobraria a espessura numa
   linha só. */
.maze-cell.wall-n{ border-top-width: 3px; }
.maze-cell.wall-w{ border-left-width: 3px; }
.maze-cell.wall-s{ border-bottom-width: 3px; }
.maze-cell.wall-e{ border-right-width: 3px; }

/* Estilo de parede é escolhido por WALL_STYLE (game.js), que marca
   .maze-grid com data-wall-style — só o bloco cujo atributo bate entra em
   jogo; sem o atributo (ou "flat") fica no visual original acima (4 regras
   .wall-n/w/s/e, var(--border) sólida 3px). Depois de decidir qual usar,
   apagar os 3 blocos não escolhidos e, se quiser, fixar as cores/larguras
   direto nas regras originais. */

/* 'bevel' — mesma cor do tema (var(--border)); border-style:ridge faz o
   navegador sombrear sozinho (claro num lado, escuro no oposto), sem
   precisar de uma segunda cor manual. */
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-n,
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-w,
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-s,
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-e{
  border-style: ridge;
}
.maze-grid[data-wall-style="bevel"] .maze-cell{ border-radius: 2px; }
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-n{ border-top-width: 4px; }
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-w{ border-left-width: 4px; }
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-s{ border-bottom-width: 4px; }
.maze-grid[data-wall-style="bevel"] .maze-cell.wall-e{ border-right-width: 4px; }

/* 'wood' — cerca de troncos: reaproveita o marrom já usado na orelha da
   ovelha/poste do cercado (game.js); mesmo truque ridge pra ganhar volume
   de tora sem gradiente manual. */
.maze-grid[data-wall-style="wood"] .maze-cell.wall-n,
.maze-grid[data-wall-style="wood"] .maze-cell.wall-w,
.maze-grid[data-wall-style="wood"] .maze-cell.wall-s,
.maze-grid[data-wall-style="wood"] .maze-cell.wall-e{
  border-style: ridge;
  border-color: #8B6142;
}
.maze-grid[data-wall-style="wood"] .maze-cell{ border-radius: 3px; }
.maze-grid[data-wall-style="wood"] .maze-cell.wall-n{ border-top-width: 4px; }
.maze-grid[data-wall-style="wood"] .maze-cell.wall-w{ border-left-width: 4px; }
.maze-grid[data-wall-style="wood"] .maze-cell.wall-s{ border-bottom-width: 4px; }
.maze-grid[data-wall-style="wood"] .maze-cell.wall-e{ border-right-width: 4px; }

/* 'hedge' — sebe: verde com border-style:double (duas linhas finas lado a
   lado), lembrando fileira de arbusto em vez de parede sólida. */
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-n,
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-w,
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-s,
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-e{
  border-style: double;
  border-color: #4C7A3D;
}
.maze-grid[data-wall-style="hedge"] .maze-cell{ border-radius: 4px; }
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-n{ border-top-width: 6px; }
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-w{ border-left-width: 6px; }
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-s{ border-bottom-width: 6px; }
.maze-grid[data-wall-style="hedge"] .maze-cell.wall-e{ border-right-width: 6px; }

/* Caminho-solução revelado (Desistir ou sem mais tentativas — ver
   revealSolutionPath() em game.js). Tom neutro/discreto de propósito: não é
   uma conquista, é só uma consolação depois que a fase já terminou — vale
   pra célula de chegada também, sem override: o ícone de cercado continua
   sem fundo próprio. */
.maze-cell.path-reveal{
  background: rgba(217,164,65,0.16);
}

/* --- tokens (ovelha / lobos) --------------------------------------------
   Posicionados de forma absoluta dentro de .maze-grid (position:relative),
   movidos via transform:translate() calculado em game.js a partir da
   posição real (em pixels) da célula-alvo — a transição CSS cuida da
   animação de deslizar de uma célula pra outra, sem precisar de rAF/JS por
   frame. `font-size` é sempre definido inline por positionToken() (game.js),
   proporcional ao tamanho REAL da célula — uma % em CSS aqui calcularia em
   cima do font-size herdado do <body>, não do tamanho da célula, e o token
   nasceria sempre do mesmo tamanho minúsculo em qualquer fase. */
.token{
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  pointer-events: none;
  transition: transform .12s ease-out;
  will-change: transform;
}

/* Modo 'head' da ovelha (ver SHEEP_HEAD_SVG em game.js) — `<svg>` é
   inline por padrão e ganha uns pixels extras de espaço embaixo (reservado
   pra "descender" de texto); display:block tira essa folga fantasma, que
   senão desalinhava o ícone um pouco pra cima do centro real da célula. */
.token svg{ display: block; }

.token-sheep{ z-index: 2; }

/* MÓDULO DE LOBOS: única regra CSS específica de lobo do projeto — seguro
   apagar esta linha (só ela) se os lobos forem removidos do jogo (ver
   README, "Como remover os lobos"); nada mais aqui depende dela.
   filter replica a mesma sombra do SHEEP_HEAD_SVG (game.js), já que o
   lobo é renderizado como emoji puro (sem <svg> próprio pra carregar
   o drop-shadow inline). */
.token-wolf{ z-index: 1; opacity: .92; filter: drop-shadow(0 2px 4px rgba(0,0,0,.25)); }

/* Flash de "colisão" no token da ovelha — anima `filter`, não `transform`,
   de propósito: `transform` já é usado (inline, por game.js) pra
   posicionar o token via translate(); animar a MESMA propriedade por CSS
   entraria em conflito e faria o token pular pra origem durante o flash. */
.token-sheep.hit{
  animation: letmaze-hit-flash .3s ease;
}

@keyframes letmaze-hit-flash{
  0%, 100%{ filter: none; }
  50%{ filter: drop-shadow(0 0 5px var(--red-ink)) brightness(1.35); }
}

.modal-card.modal-card-howto{
  width: min(360px, 92%);
  max-width: 360px;
  max-height: 82vh;
  overflow-y: auto;
  text-align: left;
  padding: 20px 22px;
}
.howto-body p{ margin: 0 0 10px; font-size: 14px; color: var(--modal-ink); line-height: 1.5; }
.howto-section{
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--modal-accent);
  margin: 16px 0 8px;
  font-weight: 600;
}
.howto-section:first-child{ margin-top: 0; }
.howto-list{ margin: 0 0 10px; padding-left: 18px; font-size: 14px; color: var(--modal-ink); line-height: 1.5; }
.howto-list li{ margin-bottom: 4px; }

.stat-accent{
  position: relative;
  overflow: hidden;
}
.stat-accent::before{
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--accent-ink);
}

.stat-row .stat-row-btn{
  flex: 1;
  min-width: 0;
  height: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
#btn-give-up.disabled{
  opacity: .4;
  pointer-events: none;
}

/* Eventos no log lateral (ver #event-log em game.js) — reaproveita as
   mesmas classes visuais de #word-log do Letfall/Letrix (bônus/repetido),
   mas com semântica própria: 'good' = fase concluída, 'bad' = esbarrão em
   lobo ou desistência. */
#event-log{ list-style: none; margin: 0; padding: 0; font-family: var(--font-type); font-size: 13px; }
#event-log li{ padding: 4px 0; border-bottom: 1px dashed var(--log-divider); }
#event-log li.good{ color: var(--accent-ink); font-weight: 600; }
#event-log li.bad{ color: var(--red-ink); }

.modal-actions{ flex-wrap: wrap; }

/* Em mobile, FASE/PONTUAÇÃO/MOVIMENTOS precisam ficar ACIMA do tabuleiro —
   mas são filhos de .side-panel, que no DOM vem DEPOIS de .board-column,
   então nenhum flex-direction/order dá pra puxar só esses três pra cima
   sem também mover o resto do painel (tempo, vidas, log). Mesma solução do
   Letrix/Letfall: vira grid nomeado no .layout, e .side-panel/.board-column/
   .stat-row somem da árvore de caixas via display:contents — só os cards
   folha (phase-stat, score-stat...) e o board-wrap sobram como itens
   diretos do grid, cada um plugado na área certa. */
@media (max-width: 620px){
  .layout{
    display: grid;
    /* 6 colunas = MMC(2, 3): fase/pontuação/movimentos dividem a mesma
       linha em terços (2 colunas cada); as linhas de 2 itens (tempo/total,
       tentativas/desistir) continuam metade cada (3 colunas). Nenhuma regra
       `grid-area:` dos cards muda — só este mapa. */
    grid-template-columns: repeat(6, 1fr);
    grid-template-areas:
      "phase phase score score moves moves"
      "board board board board board board"
      "time  time  time  total total total"
      "lives lives lives giveup giveup giveup"
      "log   log   log   log   log   log";
    gap: var(--mobile-grid-gap);
  }
  .side-panel{ display: contents; }
  .board-column{ display: contents; }
  .stat-row{ display: contents; }

  #phase-stat{ grid-area: phase; margin: 0; }
  /* scroll-margin-top: usado pelo scrollIntoView() do toque no labirinto
     (ver game.js) — folga acima do bloco em vez de colar na borda da
     viewport (mesmo padrão do #next-letter-stat do Letfall/#score-stat do
     Letrix). */
  #score-stat{ grid-area: score; margin: 0; scroll-margin-top: 8px; }
  #moves-stat{ grid-area: moves; margin: 0; }
  .board-wrap{ grid-area: board; margin: 0; max-width: none; width: 100%; }
  #time-stat{ grid-area: time; margin: 0; }
  #total-time-stat{ grid-area: total; margin: 0; }
  #lives-stat{ grid-area: lives; margin: 0; }
  #btn-give-up{ grid-area: giveup; margin: 0; }
  .log{ grid-area: log; margin: 0; }

  #phase-stat, #score-stat, #moves-stat, #time-stat, #total-time-stat,
  #lives-stat, #btn-give-up, .log, .board-wrap{
    min-width: 0;
  }

  .mobile-controls{ margin-top:12px; }
}
