/* =====================================================================
   Monkey Climb — capa de interfaz en DOM (css/ui.css)
   =====================================================================
   Fase 1 de la migración de menús: de dibujados en <canvas> a HTML/CSS
   real, encima del canvas. Esta hoja todavía no tiene consumidores reales
   (esos llegan en las fases siguientes) — acá solo viven los TOKENS
   (copiados tal cual de mockup.html, sin inventar variantes) y los
   COMPONENTES BASE reutilizables: .glass, .pill, .tile, .row, .dot
   (el "badge" del pedido), .cta, .modes/.mode (el "segmented").

   Por qué una hoja aparte y no el <style> inline de index.html: ese
   inline es del shell de la página (overlay de "girá el celular", diálogo
   de nombre, canvas) y no depende de fuentes externas ni cambia con cada
   fase de este trabajo — separarlo evita mezclar dos ciclos de vida
   distintos en el mismo bloque, y le da su propio cache-buster.
   ===================================================================== */

:root {
  /* fondos y superficies */
  --ground: #05070e;
  --ground-2: #0a0e18;
  --glass-top: rgba(22, 28, 46, .82);
  --glass-bot: rgba(8, 11, 20, .90);
  --glass-top-hi: rgba(30, 38, 60, .90);
  --hairline: rgba(255, 255, 255, .10);
  --hairline-hi: rgba(255, 255, 255, .18);
  --inner-hi: rgba(255, 255, 255, .09);

  /* texto */
  --ink: #eef2fa;
  --ink-2: #aeb8cc;
  --ink-3: #6f7a90;

  /* acentos */
  --gold: #ffd95a;
  --gold-deep: #ffc72e;
  --gold-ink: #1a1206;
  --green: #6fe08a;
  --alert: #e63f2d;
  --hell: #ff7a4d;

  /* radios */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-pill: 999px;

  /* tipografías */
  --display: "Fredoka", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --label: "Barlow Condensed", "Barlow", system-ui, sans-serif;
  --body: "Barlow", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
}

/* ---------------------------------------------------------------------
   Capa #ui: vive encima del canvas, debajo de los overlays que ya
   existían en index.html (girá-el-celular en 20000, diálogo de nombre en
   20001) — por eso un z-index chico alcanza, ninguna pantalla nueva tiene
   que competir con esos dos.

   #ui ES el espacio escalado de la maqueta — no cada pantalla por su
   cuenta. Se posiciona exacto sobre el rect real del canvas y se estira
   con transform:scale(var(--ui-scale)), el mismo principio que ya usa el
   canvas (ctx.setTransform con scale=altoReal/altoLógico) — ver
   updateUIStage() en viewport.js, que es quien pone left/top/width/
   height y la variable --ui-scale en cada resize. height/width de acá
   son solo el fallback antes de que esa función corra la primera vez
   (corre sincrónico al cargar el módulo, así que en la práctica el
   usuario nunca llega a ver este valor crudo).

   Con esto, TODO lo que cuelga de #ui usa píxeles de la maqueta tal
   cual (left:56px, top:70px, font-size:44px...) sin traducir ni un
   número — 1px de mockup.html es 1px de acá adentro, a cualquier
   resolución.

   pointer-events:none en el contenedor es lo que mantiene vivo el easter
   egg de la luna (checkMoonTap en main.js, escucha toques directo sobre
   el canvas en el título): si #ui capturara todo el puntero, ningún toque
   llegaría nunca al canvas mientras el título está en pantalla. Cada
   pantalla/elemento REALMENTE interactivo tiene que reactivar
   pointer-events:auto a mano — nunca al revés (nunca pointer-events:auto
   por default en un contenedor grande "por las dudas").
   --------------------------------------------------------------------- */
#ui {
  position: fixed;
  left: 0;
  top: 0;
  height: 525px;
  width: 100vw;
  transform-origin: top left;
  transform: scale(var(--ui-scale, 1));
  z-index: 10;
  pointer-events: none;
  font-family: var(--body);
  color: var(--ink);
  /* El canvas ahora va de borde a borde SIEMPRE (ver resizeCanvas en
     viewport.js) — las safe areas ya no le recortan nada al canvas, así
     que tienen que vivir acá: padding, no un canvas más chico. Dividido
     por --ui-scale porque este padding se define en el espacio de
     MAQUETA de #ui (pre-transform), no en px reales de pantalla — un
     notch de 44px reales tiene que convertirse en menos "px de maqueta"
     cuanto más zoomeada esté la interfaz, si no el margen real crece o
     se achica con la resolución en vez de quedarse fijo en 44px.
     position:absolute (y position:fixed, porque transform en un
     ancestro convierte a #ui en containing block también para fixed)
     usan el padding-box del ancestro posicionado más cercano como
     referencia — por eso alcanza con este único padding para que TODO
     lo de acá adentro (título, HUD, notificaciones) respete el notch
     sin que ninguna regla individual (left:14px, etc.) tenga que
     saberlo. */
  padding-left: calc(var(--safe-left, 0px) / var(--ui-scale, 1));
  padding-right: calc(var(--safe-right, 0px) / var(--ui-scale, 1));
  padding-top: calc(var(--safe-top, 0px) / var(--ui-scale, 1));
  padding-bottom: calc(var(--safe-bottom, 0px) / var(--ui-scale, 1));
}
#ui * {
  box-sizing: border-box;
}
#ui [data-interactive] {
  pointer-events: auto;
}

/* ---------------------------------------------------------------------
   Componentes base — recetas visuales copiadas de mockup.html tal cual.
   A propósito SIN posición/tamaño de layout acá (el mockup los ubica con
   left/top/width fijos porque es una maqueta de una sola escena; el
   layout real de cada pantalla se resuelve en las fases siguientes, con
   flex/grid y las dos variantes de plataforma) — esto es solo la piel:
   color, borde, sombra, tipografía.
   --------------------------------------------------------------------- */

/* Vidrio: la superficie base de casi todo lo demás (pill, row, tile,
   iconbtn se apoyan en .glass además de su propia clase). */
.glass {
  background: linear-gradient(180deg, var(--glass-top), var(--glass-bot));
  border: 1px solid var(--hairline);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .45), inset 0 1px 0 var(--inner-hi);
  backdrop-filter: blur(9px);
  -webkit-backdrop-filter: blur(9px);
}

.pill {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  font-family: var(--display);
  font-weight: 500;
  font-size: 14px;
  color: var(--gold);
}

.iconbtn {
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  font-size: 14px;
  color: var(--ink-2);
}
.iconbtn.is-muted {
  color: var(--ink-3);
  opacity: .65;
}

/* Fila tipo "tile" horizontal: menú de la izquierda en PC (Skins,
   Desafíos, Tienda, Ranking). */
.row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  height: 40px;
  padding: 0 14px;
  border-radius: var(--r-md);
}
.row .ic {
  font-size: 15px;
  width: 18px;
  text-align: center;
  filter: saturate(.8);
}
.row .lb {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 12px;
  color: var(--ink-2);
}
.row .meta {
  margin-left: auto;
  font-family: var(--label);
  letter-spacing: .1em;
  font-size: 10.5px;
  color: var(--ink-3);
}
/* Fila con algo pendiente de reclamar: mismo vidrio, con un filo dorado
   apenas insinuado — no es el único dorado de la pantalla (eso lo pidió
   la maqueta reservado para el CTA), es un acento sutil, no una segunda
   voz fuerte. */
.row.hot {
  border-color: rgba(255, 217, 90, .35);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .45), inset 0 1px 0 var(--inner-hi), 0 0 0 1px rgba(255, 217, 90, .10);
}

/* Badge (el "dot" de la maqueta): el rojo SOLO vive acá — nunca en fondos
   ni bordes de ningún otro componente, ver la regla de la maqueta. */
.dot,
.badge {
  margin-left: auto;
  min-width: 19px;
  height: 19px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--alert);
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: 11px;
  color: #fff;
  box-shadow: 0 0 0 2px rgba(5, 7, 14, .9);
}

/* Tile vertical: barra inferior en celular (mismo contenido que .row,
   apilado en vez de en fila). */
.tile {
  position: relative;
  border-radius: var(--r-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
}
.tile .ic {
  font-size: 19px;
  line-height: 1;
  filter: saturate(.85);
}
.tile .lb {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 10.5px;
  color: var(--ink-2);
}
.tile .dot {
  position: absolute;
  top: -7px;
  right: -6px;
  margin: 0;
}
.tile .tmr {
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-family: var(--label);
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--ink-3);
  background: var(--ground-2);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  padding: 1px 7px;
}
.tile.hot {
  border-color: rgba(255, 217, 90, .35);
}

/* CTA: el botón JUGAR — único elemento dorado por pantalla (regla de la
   maqueta) y el único con profundidad real (el 6px de "base" del box-
   shadow), a propósito distinto de todo lo demás (vidrio, plano). */
.cta {
  border-radius: var(--r-lg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 26px;
  background: linear-gradient(180deg, #ffe58a, var(--gold-deep));
  border: 1px solid rgba(120, 80, 0, .45);
  box-shadow: 0 6px 0 rgba(140, 92, 0, .45), 0 18px 34px rgba(255, 190, 60, .16), inset 0 2px 0 rgba(255, 255, 255, .65);
}
.cta b {
  font-family: var(--display);
  font-weight: 600;
  font-size: 28px;
  letter-spacing: .04em;
  color: var(--gold-ink);
}
.cta span {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 11px;
  color: rgba(26, 18, 6, .55);
}
.cta:active {
  transform: translateY(2px);
  box-shadow: 0 4px 0 rgba(140, 92, 0, .45), 0 14px 28px rgba(255, 190, 60, .14), inset 0 2px 0 rgba(255, 255, 255, .65);
}

/* Segmentado (selector de modo: Normal / Hell / Contrarreloj / Fantasma).
   El pedido lo llama "segmented"; en la maqueta son .modes (el riel) y
   .mode (cada opción) — mismos nombres acá para poder copiar el markup
   de mockup.html sin traducir clases. */
.modes {
  display: flex;
  padding: 3px;
  border-radius: var(--r-pill);
  gap: 2px;
}
.mode {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 11px;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  color: var(--ink-3);
}
.mode.on {
  background: linear-gradient(180deg, var(--gold), var(--gold-deep));
  color: var(--gold-ink);
  font-weight: 700;
  box-shadow: 0 2px 8px rgba(255, 190, 40, .3);
}

/* Etiqueta de aviso tipo "Nuevo modo · Hell" (ver .hell en la maqueta):
   el único lugar donde el acento hell aparece, y solo como texto/borde
   fino, nunca como fondo sólido. */
.tag-hell {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: linear-gradient(180deg, rgba(58, 10, 4, .85), rgba(20, 4, 2, .9));
  border: 1px solid rgba(255, 110, 60, .42);
  box-shadow: 0 0 20px rgba(255, 80, 25, .18);
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .18em;
  font-size: 10.5px;
  color: var(--hell);
}
.tag-hell::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--hell);
  box-shadow: 0 0 8px var(--hell);
}

/* ---------------------------------------------------------------------
   Interactividad: en celular no hay :hover (un toque que no responde se
   siente roto — ver Fase 5), pero el feedback de "esto es tocable" con
   :active sí es universal. Los estados de :hover reales para desktop se
   suman por componente en las fases siguientes, cuando haya markup real
   para engancharlos.
   --------------------------------------------------------------------- */
[data-interactive] {
  cursor: pointer;
  transition: transform .12s ease;
}
[data-interactive]:active {
  transform: scale(.96);
}
/* (hover: hover) y no [data-platform="desktop"] a propósito: el override
   de plataforma es para decidir LAYOUT (para poder probar el de celular
   sentado en una PC), pero el feedback de :hover tiene que seguir la
   capacidad real del dispositivo — si alguien fuerza el layout touch en
   una PC con mouse, el mouse real igual tiene que poder hacer hover. */
@media (hover: hover) {
  .row[data-interactive]:hover,
  .tile[data-interactive]:hover,
  .mode[data-interactive]:hover,
  .iconbtn[data-interactive]:hover {
    background: rgba(255, 255, 255, .06);
  }
  .cta[data-interactive]:hover {
    filter: brightness(1.04);
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-interactive] {
    transition: none;
  }
  [data-interactive]:active {
    transform: none;
  }
}

/* =====================================================================
   Fase 2 — pantalla de inicio (ver buildTitleUI/syncTitleUI en main.js)
   =====================================================================
   Valores de posición copiados de mockup.html tal cual (izquierda 56px,
   arranques en 70/196/238/322px...) — el layout es a propósito en píxeles
   fijos desde los bordes, no proporcional al viewport: es el mismo idioma
   que ya usaba la maqueta en su escena de referencia.

   .title-menu (fila horizontal, PC) y .title-navbar (tiles apilados,
   celular) son DOS árboles de nodos separados para los mismos 4 accesos
   (Skins/Desafíos/Tienda/Ranking) — no una sola lista reposicionada por
   CSS: la fila y el tile tienen estructura interna demasiado distinta
   (el badge del tile es position:absolute en una esquina, el de la fila
   es inline al final) como para que compartir nodos valiera la pena
   frente a simplemente duplicar el marcado y ocultar el que no toca.
   --------------------------------------------------------------------- */
.screen-title {
  position: absolute;
  inset: 0;
  display: none;
}
.screen-title.is-visible {
  display: block;
}
/* Mientras hay un panel de canvas abierto (Skins/Desafíos/Tienda/Ranking
   — todavía dibujados en <canvas>, ver Fase 3) esta pantalla se esconde
   entera: si no, sus botones (aunque no interactivos ahí abajo) tapan
   visualmente el panel que se dibuja encima en el canvas. */
.screen-title.panel-open {
  display: none;
}
/* El panel de admin (canvas, ver drawAdminPanel en main.js) se ancla
   arriba a la derecha — exactamente donde viven .title-topright
   (bananas/sonido/pantalla completa) y .title-stats (mejor altura/mejor
   score), que son DOM y por eso quedan SIEMPRE pintados encima del
   panel, sin importar el orden de dibujo del canvas. A diferencia de
   .panel-open (arriba), acá NO se esconde toda la pantalla — el resto
   del título sigue anduviendo mientras se ajusta algo en el panel. */
.screen-title.admin-open .title-topright,
.screen-title.admin-open .title-stats {
  display: none;
}

/* El admin se dibuja en canvas: ninguna pantalla DOM de atrás debe
   taparlo ni recibir sus toques. PIN/editor propios siguen utilizables. */
#ui.admin-panel-open > :not(.screen-pin) {
  visibility: hidden;
}
#ui.admin-panel-open > :not(.screen-pin),
#ui.admin-panel-open > :not(.screen-pin) * {
  pointer-events: none !important;
}

/* ===================== Pantalla de título =====================
   TODO acá abajo son hijos DIRECTOS de .screen-title, cada uno con su
   propia position:absolute en píxeles de MAQUETA tal cual — sin traducir
   ningún número. El único espacio que escala es #ui mismo
   (transform:scale(var(--ui-scale)), ver el comentario junto a #ui y
   updateUIStage() en viewport.js) — por eso ninguna zona necesita su
   propia escala ni su propio fit: top-left (aviso de modo nuevo),
   top-right (bananas + controles), la columna de la izquierda (marca,
   modos, JUGAR, menú, ayudas) y right-bottom (récords) conviven en el
   MISMO espacio de coordenadas, así que quedan siempre del mismo
   "peso" entre sí sin importar la resolución. El resto de la pantalla
   queda libre para el arte. */

/* Velo direccional: entre el canvas y el contenido de esta pantalla
   (primer hijo de .screen-title, pinta detrás de todo lo demás por orden
   de DOM). Oscurece el lado del menú y deja limpio el lado del arte, así
   el texto de la izquierda siempre tiene contraste sin importar qué haya
   de fondo (cielo claro, una nube, troncos).

   Los valores bajaron (antes .90 en desktop y .72/.88 en los extremos de
   celular): con esos números la selva del fondo quedaba prácticamente
   negra y la portada se leía como una pantalla apagada, no como un menú
   sobre el juego. El contraste del texto NO depende ahora solo del velo
   — el logo, la bajada y los links legales llevan su propia sombra (ver
   .title-logo / .title-tagline / .title-legal más abajo), que es lo que
   permite aclarar el fondo sin perder legibilidad. */
.title-veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, rgba(3, 5, 11, .74) 0%, rgba(3, 5, 11, .46) 34%, rgba(3, 5, 11, .08) 62%, transparent 80%);
}
[data-platform="touch"] .title-veil {
  background: linear-gradient(180deg, rgba(3, 5, 11, .52) 0%, rgba(3, 5, 11, .10) 40%, rgba(3, 5, 11, .34) 78%, rgba(3, 5, 11, .68) 100%);
}

/* top-left: avisos de modo nuevo (HELL y MAREA), apilados. top:14 en vez del
   36 de la maqueta para que la de MAREA (top:40) entre debajo sin pisar la
   marca (top:70). */
.title-hell {
  position: absolute;
  left: 56px;
  top: 14px;
  display: none;
  align-items: center;
  gap: 7px;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: linear-gradient(180deg, rgba(58, 10, 4, .85), rgba(20, 4, 2, .9));
  border: 1px solid rgba(255, 110, 60, .42);
  box-shadow: 0 0 20px rgba(255, 80, 25, .18);
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .18em;
  font-size: 10.5px;
  color: var(--hell);
}
.title-hell.is-visible {
  display: inline-flex;
}
.title-hell::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--hell);
  box-shadow: 0 0 8px var(--hell);
  animation: title-hell-breathe 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .title-hell::before { animation: none; }
}
@keyframes title-hell-breathe {
  0%, 100% { opacity: .45; }
  50% { opacity: 1; }
}
[data-platform="touch"] .title-hell {
  left: 14px;
  top: 12px;
}

/* Píldora de MAREA — mismo patrón que .title-hell pero en violeta y apilada
   justo debajo (top:40 vs top:14). El usuario pidió mantener las dos. */
.title-flood {
  position: absolute;
  left: 56px;
  top: 40px;
  display: none;
  align-items: center;
  gap: 7px;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: linear-gradient(180deg, rgba(38, 12, 58, .85), rgba(14, 4, 24, .9));
  border: 1px solid rgba(170, 110, 255, .42);
  box-shadow: 0 0 20px rgba(150, 90, 255, .18);
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .18em;
  font-size: 10.5px;
  color: #c9a6ff;
}
.title-flood.is-visible {
  display: inline-flex;
}
.title-flood::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #c9a6ff;
  box-shadow: 0 0 8px #b98cff;
  animation: title-hell-breathe 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .title-flood::before { animation: none; }
}
[data-platform="touch"] .title-flood {
  left: 14px;
  top: 40px;
}

/* top-right: bananas + controles — left/top crudos de la maqueta
   (.topright{right:20px;top:18px}). Ya no necesita JS para no taparle la
   luna: al vivir en el mismo espacio escalado que el canvas, right:20px
   siempre queda a la misma distancia PROPORCIONAL del borde derecho real
   que en la maqueta, sin importar la resolución. */
.title-topright {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 8px;
  top: 18px;
  right: 20px;
}
.title-topright .pill {
  white-space: nowrap;
}
.title-coin-pill {
  appearance: none;
  height: 36px;
  padding: 0 5px 0 12px;
  gap: 9px;
  border: 1px solid rgba(255, 217, 90, .22);
  background: linear-gradient(180deg, rgba(24, 31, 49, .96), rgba(8, 12, 22, .96));
  color: inherit;
  font: inherit;
  cursor: pointer;
  overflow: hidden;
  box-shadow: 0 9px 24px rgba(0, 0, 0, .48), inset 0 1px rgba(255,255,255,.08);
}
.title-coin-pill .coin-amount {
  min-width: 46px;
  color: #f4f7fc;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.coin-plus {
  position: relative;
  flex: 0 0 auto;
  width: 27px;
  height: 26px;
  margin-left: 1px;
  border: 1px solid rgba(115, 76, 0, .55);
  border-radius: 9px;
  background: linear-gradient(180deg, #ffe98f, #ffc93c);
  box-shadow: inset 0 1px rgba(255,255,255,.72), 0 3px 9px rgba(255,190,40,.2);
}
.coin-plus::before,
.coin-plus::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 11px;
  height: 2.5px;
  border-radius: 2px;
  background: #342305;
  transform: translate(-50%, -50%);
}
.coin-plus::after {
  transform: translate(-50%, -50%) rotate(90deg);
}
@media (hover: hover) {
  .title-coin-pill:hover {
    border-color: rgba(255, 217, 90, .5);
  }
  .title-coin-pill:hover .coin-plus {
    filter: brightness(1.06);
  }
}
.title-topright .pill img,
.title-topright .pill svg {
  width: 16px;
  height: 16px;
  display: block;
}
/* Sin esto un <svg> inline se renderiza a su tamaño intrínseco (300x150
   por spec cuando no tiene width/height propios) — el glyph, dibujado en
   un viewBox de 0-24, queda como un punto invisible en la esquina de esa
   caja enorme. Es por lo que los íconos de sonido/pantalla completa se
   veían como cajas de vidrio vacías. */
.iconbtn svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* left-column: marca, modos, JUGAR, menú, ayudas de teclado — cada uno
   con su propia position:absolute (left/top de maqueta), SIN wrapper ni
   escala propia. */
.title-brand {
  position: absolute;
  left: 56px;
  top: 70px;
  width: 330px;
}
.title-logo {
  font-family: var(--display);
  font-weight: 600;
  font-size: 44px;
  line-height: .98;
  letter-spacing: .005em;
  margin: 0;
  color: #fff;
  /* Dos sombras con trabajos distintos: la primera es el halo dorado de
     siempre (estética), la segunda es contraste real contra el fondo —
     la que permite que .title-veil sea más suave y se vea la selva. */
  text-shadow: 0 0 34px rgba(255, 217, 90, .30), 0 2px 12px rgba(0, 0, 0, .85);
}
.title-logo em {
  font-style: normal;
  display: block;
  color: var(--gold);
}
.title-tagline {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .30em;
  font-size: 11px;
  color: var(--ink-3);
  margin: 12px 0 0;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .9);
}

/* 5 modos reales (GAME_MODES tiene "hardcore" además de los 4 de la
   maqueta) no entran en una sola fila de 296px ni siquiera a tamaño
   nativo — envolver a una segunda fila en vez de desbordar. r-md en vez
   de r-pill en el contenedor: con dos filas, una píldora completa se ve
   rara (un óvalo alargado en vez de un grupo de pastillitas). */
.title-modes {
  position: absolute;
  left: 56px;
  top: 196px;
  width: 296px;
  flex-wrap: wrap;
  border-radius: var(--r-md);
}
/* padding/tracking apretados: con "Marea" el segmentado pasó a 6 modos + la
   pastilla MP, y el presupuesto de 2 filas de width:296px estaba medido para
   5. Menos aire por chip para que no salte a una 3ª fila y pise a JUGAR. */
.title-modes .mode {
  padding: 5px 8px;
  letter-spacing: .04em;
}
/* Comparte el segmentado de modos (mismo flex-wrap, mismo presupuesto de
   2 filas ya medido para los 5 modos) pero NO es un modo — es una
   navegación (abre el lobby), así que nunca lleva .on ni compite por el
   dorado de "seleccionado". La diferencia con sus vecinos es el ÍCONO
   (ver ICONS.multiplayer en main.js — el mismo que ya usa este ítem en
   .row/.tile), no un color ni un borde propios: con el mismo gris apagado
   var(--ink-3) que cualquier .mode sin seleccionar (heredado, ver .mode
   arriba) se integra al resto de la fila en vez de leerse como un chip
   aparte — antes usaba var(--ink), el tono más brillante de la paleta,
   junto con un borde individual que ninguno de sus vecinos lleva, y esa
   combinación era justo lo que lo hacía "verse distinto" (reportado). El
   ícono corre por currentColor, así que sigue ese mismo gris en reposo y
   se aclara solo con el :hover ya compartido por todo .mode[data-interactive]
   (ver la regla @media(hover:hover) más abajo) — ninguna regla nueva para
   eso tampoco. */
.title-modes .mode-multiplayer {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
/* Mismo patrón que .title-menu .row .ic svg/.title-navbar .tile .ic svg
   arriba: tamaño fijo en px + display:block, no un porcentaje — un ícono
   inline SVG sin esto hereda el alto de línea del texto de al lado y
   queda descentrado un par de px. */
.title-modes .mode-multiplayer .ic svg {
  width: 11px;
  height: 11px;
  display: block;
}
/* En celular el acceso a Multijugador sigue siendo solo el tile de
   title-navbar (barra inferior, sin cambios) — sumar esta pastilla
   también ahí lo hubiera duplicado. En touch .title-modes pasa a
   width:auto/nowrap (ver más abajo) pensado para los 5 modos reales
   solos; esconder la pastilla achica de nuevo qué tiene que entrar en
   esa fila, respetando el pedido de "esto es solo layout de PC". */
[data-platform="touch"] .title-modes .mode-multiplayer {
  display: none;
}

/* top:262 en vez del 238 crudo de la maqueta — la maqueta ubicaba JUGAR
   asumiendo el segmentado en UNA fila. Con 5 modos reales envueltos en
   DOS (arriba), el selector mide ~54px de alto en vez de los ~29px de
   una fila sola, y terminaba pisando a JUGAR (por eso se veía "enorme":
   dibujado encima del segundo renglón del segmentado). 262 deja el
   mismo margen de aire que tenía la maqueta, medido desde donde el
   segmentado de DOS filas termina de verdad. */
.title-cta {
  position: absolute;
  left: 56px;
  top: 262px;
  width: 296px;
  height: 66px;
}

/* Envoltorio de modos+JUGAR (ver buildTitleUI en main.js) — en desktop
   es invisible al layout: display:contents saca el propio wrapper de la
   caja, sus hijos siguen siendo hijos efectivos de .screen-title con su
   position:absolute de siempre, sin cambiar un solo número de acá arriba.
   Touch lo reaprovecha como un flex real (ver más abajo). */
.title-central {
  display: contents;
}

/* top:340 en vez del 322 crudo — corrido la misma distancia que JUGAR
   (arriba), para no pisarlo. */
.title-menu {
  position: absolute;
  left: 56px;
  top: 340px;
  width: 296px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* .row de mockup mide 40px — con las 4 filas reales (Skins/Desafíos/
   Tienda/Ranking, la maqueta mostraba menos) achicarla un poco (sin
   tocar el resto de la receta: radio, ícono, tipografía) deja lugar para
   las ayudas de teclado debajo sin superponerse. .row en otros lados
   (paneles de Fase 3) sigue midiendo 40px. */
.title-menu .row {
  height: 34px;
}
.title-menu .row .ic svg {
  width: 15px;
  height: 15px;
  display: block;
}

/* top:498 en vez del bottom:30 crudo de la maqueta — con las 4 filas
   reales del menú (top:340 + 4×34px + 3×4px de gap = termina en 488),
   .keys{bottom:30px} de la maqueta (pensada para un menú más corto y un
   segmentado de una fila) quedaría pisando el menú: se pisan a
   CUALQUIER escala (un transform:scale() no puede arreglar una
   superposición entre dos elementos posicionados de forma
   independiente). 498 es simplemente "10px después de donde termina el
   menú real" — ver UI_STAGE_H en viewport.js (por eso el presupuesto de
   PC es 525 y no 476: si no, ni esto entraría). */
.title-keys {
  position: absolute;
  left: 56px;
  top: 498px;
  font-family: var(--body);
  font-size: 11.5px;
  color: var(--ink-3);
  letter-spacing: .02em;
}
.title-keys kbd {
  font-family: var(--body);
  font-size: 10.5px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 1px 6px;
  color: var(--ink-2);
  margin: 0 2px;
}

.title-navbar {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 80px;
  gap: 10px;
  align-items: center;
  justify-content: center;
  padding: 0 16px 10px;
  background: linear-gradient(180deg, rgba(6, 9, 17, 0), rgba(4, 6, 12, .92) 46%);
}
.title-navbar .tile {
  width: 120px;
  height: 58px;
}
.title-navbar .tile .ic svg {
  width: 19px;
  height: 19px;
  display: block;
}

/* Receta compartida de "récord" (etiqueta Condensed arriba, número
   Fredoka abajo) — la usa .title-stats acá y .hud-bottomright en el HUD
   de partida (ver más abajo), cada uno solo con su propia posición. */
.stats .k {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .2em;
  font-size: 10px;
  color: var(--ink-3);
}
.stats .v {
  font-family: var(--display);
  font-weight: 500;
  font-size: 27px;
  line-height: 1.12;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.stats .v em {
  font-style: normal;
  font-size: 13px;
  color: var(--ink-3);
  margin-left: 4px;
}
.stats .v.g {
  color: var(--green);
}
.stats .r + .r {
  margin-top: 15px;
}

/* right-bottom: récords — left/top crudos de la maqueta
   (.stats{right:44px;bottom:34px}). */
.title-stats {
  position: absolute;
  right: 44px;
  bottom: 34px;
  text-align: right;
}
.title-legal {
  position: absolute;
  right: 42px;
  bottom: 10px;
  display: flex;
  gap: 7px;
  align-items: center;
  color: var(--ink-3);
  font: 500 10px/1 var(--body);
  text-shadow: 0 1px 6px rgba(0, 0, 0, .9);
}
.title-legal a { color: inherit; text-decoration: none; }
.title-legal a:hover, .title-legal a:focus-visible { color: var(--gold); text-decoration: underline; }
/* "Cómo jugar" se despega del resto de la fila: privacidad y términos son
   letra chica obligatoria, esto es lo único que le sirve a alguien que abre
   el juego por primera vez y no sabe ni cómo se mueve el mono. */
.title-legal .title-help-link {
  color: var(--ink-2);
  font-weight: 600;
}

/* ---------------- variante celular (data-platform, ver viewport.js) ---
   En touch la composición YA NO usa top fijos copiados de la maqueta de
   celular (.stage.m, 800×370): esos números solo salen bien cuando la
   pantalla real tiene la MISMA proporción que esa maqueta, y ningún
   iPhone la tiene. Con top fijos, cualquier otra proporción deja el
   bloque de arriba apretado contra el borde y un hueco enorme antes de
   la barra de accesos (o al revés) — un bug real, reportado con capturas
   en 844×390/667×375/932×430.

   La receta ahora es una columna flex de alto completo en .screen-title:
   arriba la marca, abajo la barra de accesos (su propio alto, sin
   posición fija), y en el medio title-central (modos+JUGAR) con flex:1
   para que se centre solo en lo que sobra. Así entra bien en CUALQUIER
   proporción de pantalla sin depender de que se parezca a la maqueta —
   que era exactamente el pedido. */
[data-platform="touch"] .screen-title.is-visible {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding-top: 16px;
}
[data-platform="touch"] .screen-title.panel-open {
  display: none;
}
/* El aviso de "Nuevo modo · Hell" pasa a ser parte del flujo (en vez de
   position:absolute clavado en la esquina), pero sigue pegado a la
   IZQUIERDA como siempre (align-self:flex-start) — lo único que cambia
   es que ahora reserva su propio lugar en la columna en vez de flotar
   encima de lo que haya debajo. Con la marca centrada a todo el ancho,
   un badge absoluto en la esquina quedaba en el mismo renglón que el
   logo y se lo comía ("MONKEY" se leía "...EY CLIMB"); en flujo, cuando
   está visible empuja la marca hacia abajo, así nunca se pisan sin
   importar cuánto mida el texto del logo. */
[data-platform="touch"] .title-hell {
  position: static;
  align-self: flex-start;
  margin: 6px 0 4px 22px;
}
/* Igual que .title-hell en touch: en flujo, apilada debajo, empuja la marca
   hacia abajo sin solaparse. Pisa el left/top absolutos de la regla base. */
[data-platform="touch"] .title-flood {
  position: static;
  left: auto;
  top: auto;
  align-self: flex-start;
  margin: 0 0 8px 22px;
}
/* width:auto pisa el 330px de la columna de escritorio (.title-brand
   base, más arriba) — sin esto el texto se centraba ADENTRO de esa caja
   angosta pegada a la izquierda en vez de centrarse en el ancho real de
   la pantalla, y el título quedaba corrido hacia la izquierda en vez de
   arriba del mono. */
[data-platform="touch"] .title-brand {
  position: static;
  width: auto;
  text-align: center;
}
[data-platform="touch"] .title-logo {
  font-size: 36px;
}
[data-platform="touch"] .title-logo em {
  display: inline;
}
[data-platform="touch"] .title-tagline {
  margin-top: 8px;
  letter-spacing: .26em;
  font-size: 10px;
}
/* Modos+JUGAR como UN bloque dentro del espacio libre entre la marca y
   la barra de accesos (flex:1) — ver title-central más arriba (base,
   display:contents en desktop). justify-content:flex-end en vez de
   center: centrar matemáticamente en TODO el sobrante parte el hueco en
   dos mitades iguales (arriba de los modos y abajo de JUGAR) — en una
   pantalla donde marca+navbar no llenan mucho, esas dos mitades salen
   grandes, y la de abajo queda vacía de verdad (nada la ocupa, es la
   que se reportó como "espacio de más"). flex-end junta modos+JUGAR
   contra la barra de accesos (con el padding de abajo como único aire)
   y manda TODO el sobrante arriba, contra la marca — ahí sí hay algo que
   lo llena: el mono, dibujado en canvas justo debajo del logo. */
[data-platform="touch"] .title-central {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  flex: 1 1 auto;
  min-height: 0;
  gap: 16px;
  padding: 4px 16px 22px;
}
[data-platform="touch"] .title-modes {
  position: static;
  flex-shrink: 0;
  /* En touch hay mucho más ancho disponible que los 296px de la columna
     de PC (ver .title-modes de base) — heredar ese ancho angosto era lo
     que forzaba el envoltorio a dos filas. width:auto + nowrap: el
     selector hace lo que tiene que hacer (una sola fila) porque el
     espacio le alcanza, no porque se lo obligue con overflow-x. El
     overflow-x:auto queda como red de seguridad, no como plan A. */
  width: auto;
  max-width: 94%;
  flex-wrap: nowrap;
  overflow-x: auto;
}
[data-platform="touch"] .title-cta {
  position: static;
  flex-shrink: 0;
  width: 320px;
  height: 64px;
  justify-content: center;
  gap: 14px;
}
[data-platform="touch"] .title-topright {
  top: 12px;
  right: 14px;
}
[data-platform="touch"] .title-topright .pill {
  height: 32px;
  padding-left: 9px;
  padding-right: 4px;
  gap: 6px;
  font-size: 13px;
}
[data-platform="touch"] .title-coin-pill .coin-amount {
  min-width: 38px;
}
[data-platform="touch"] .coin-plus {
  width: 24px;
  height: 24px;
  border-radius: 8px;
}

/* Anuncios recompensados: modal DOM para que el estado del proveedor, el
   límite y los errores sean legibles también en pantallas chicas. */
.rewarded-modal {
  position: absolute;
  inset: 0;
  z-index: 80;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 18px;
}
.rewarded-modal.is-visible { display: flex; }
.rewarded-scrim {
  position: absolute;
  inset: 0;
  background: rgba(2, 4, 10, .84);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}
.rewarded-card {
  position: relative;
  width: min(390px, calc(100% - 20px));
  padding: 28px 24px 22px;
  border: 1px solid rgba(255, 217, 90, .32);
  border-radius: 22px;
  background: linear-gradient(160deg, #1a2235, #090e19 72%);
  box-shadow: 0 24px 80px rgba(0, 0, 0, .65), inset 0 1px rgba(255,255,255,.08);
  text-align: center;
  pointer-events: auto;
}
.rewarded-card h2 {
  margin: 0 32px 10px;
  color: #fff;
  font: 600 27px/1.1 var(--display);
}
.rewarded-detail {
  margin: 0 auto 14px;
  color: #cbd5e5;
  font: 400 15px/1.45 var(--body);
}
.rewarded-quota {
  margin: 0 0 10px;
  color: #ffe28b;
  font: 600 13px/1.3 var(--body);
}
.rewarded-message {
  min-height: 34px;
  margin: 0 0 10px;
  color: #9fabc0;
  font: 400 12px/1.35 var(--body);
}
.rewarded-action { width: 100%; min-height: 48px; }
.rewarded-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 38px;
  min-height: 38px;
  padding: 0;
  font-size: 24px;
}
@media (max-height: 390px) {
  .rewarded-card { padding: 18px 20px 14px; }
  .rewarded-card h2 { font-size: 23px; margin-bottom: 6px; }
  .rewarded-detail { margin-bottom: 8px; font-size: 14px; }
  .rewarded-message { min-height: 18px; margin-bottom: 6px; }
}
[data-platform="touch"] .title-topright .iconbtn {
  width: 30px;
  height: 30px;
}
[data-platform="touch"] .title-topright .iconbtn svg {
  width: 18px;
  height: 18px;
}
/* Récords y ayudas de teclado desaparecen en celular — pasan a la
   pantalla de resultado (que ya los muestra), no hay lugar para ellos acá
   sin competir con el pulgar. */
[data-platform="touch"] .title-stats,
[data-platform="touch"] .title-keys,
[data-platform="touch"] .title-menu {
  display: none;
}
/* Misma razón, un caso que se había escapado: el <span> de .title-cta dice
   "Espacio" al lado de JUGAR, y un celular no tiene barra espaciadora. Al
   sacarlo, el justify-content:center de más abajo por fin centra "JUGAR" de
   verdad — antes centraba el par JUGAR+Espacio y la palabra quedaba corrida
   a la izquierda. */
[data-platform="touch"] .title-cta span {
  display: none;
}
/* Al RAS del borde inferior, no flotando arriba de la barra de accesos
   (antes bottom:82px, justo encima de ella: quedaba en medio de la
   pantalla y competía con JUGAR). La barra mide 80px y sus tiles 58,
   centrados en los 70px de su caja de contenido (padding-bottom:10) —
   o sea que ocupan de 6px a 64px contando desde abajo y dejan libre una
   franja de ~16px pegada al borde. Estos links viven ahí: centrados a
   todo el ancho (no contra un costado, donde chocarían con el tile de
   la punta) y con el safe-area de #ui ya aplicado por el padding del
   contenedor, así en un iPhone no caen debajo de la barra de gestos. */
[data-platform="touch"] .title-legal {
  left: 0;
  right: 0;
  bottom: 3px;
  justify-content: center;
  font-size: 9px;
}
[data-platform="touch"] .title-navbar {
  display: flex;
  /* Estático, adentro de la columna flex de .screen-title — ya no
     necesita left/right/bottom:0 (eso era para flotar por su cuenta
     sobre una posición absoluta); el flex la deja pegada abajo del todo
     sola, del ancho completo por el align-items:stretch del padre. */
  position: static;
  flex-shrink: 0;
}

/* ===================== HUD de partida (PLAYING) =====================
   Mismo espacio escalado que el título (#ui/--ui-scale) y las mismas
   reglas: vidrio, dorado como única voz fuerte, etiquetas Condensed
   mayúscula con tracking, números Fredoka con tabular-nums. En vez de
   una barra de losa: clusters sueltos apoyados en los bordes — entre uno
   y otro se ve el juego. Cada elemento es una zona propia, ninguna
   comparte lugar con otra (top-left, top-center, top-right,
   bottom-left, bottom-right + el chip de admin). */
.screen-hud {
  position: absolute;
  inset: 0;
  display: none;
  opacity: 0;
  transition: opacity .35s ease;
}
.screen-hud.is-visible {
  display: block;
}
.screen-hud.is-shown {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .screen-hud {
    transition: none;
  }
}
/* Mismo motivo que .screen-title.admin-open (ver más arriba): el panel
   de admin se ancla arriba a la derecha, tapado por .hud-topright
   (sonido/pantalla completa), que es DOM y queda siempre encima. */
.screen-hud.admin-open .hud-topright {
  display: none;
}

/* Pausa y game over todavía dibujan su tarjeta/botones en canvas (no
   cambió en esta pasada) — esa tarjeta se pinta DESPUÉS en una capa de
   canvas por debajo de #ui, así que si el resto del HUD (puntos/altura/
   récord/vidas/power-ups/personal/admin) se quedara visible encima se
   vería atravesado por la tarjeta en vez de detrás suyo. Se oculta
   entero en esas dos pantallas; lo único que sigue mostrándose ahí es
   .hud-topright (sonido/pantalla completa/volumen — mismos controles que
   ya tenían pausa y game over antes), salvo el botón de pausa (no tiene
   sentido con la tarjeta de PAUSA ya en pantalla). */
.screen-hud[data-hud-state="paused"] .hud-topleft,
.screen-hud[data-hud-state="paused"] .hud-topcenter,
.screen-hud[data-hud-state="paused"] .hud-powerups,
.screen-hud[data-hud-state="paused"] .hud-bottomright,
.screen-hud[data-hud-state="paused"] .hud-admin,
.screen-hud[data-hud-state="gameover"] .hud-topleft,
.screen-hud[data-hud-state="gameover"] .hud-topcenter,
.screen-hud[data-hud-state="gameover"] .hud-powerups,
.screen-hud[data-hud-state="gameover"] .hud-bottomright,
.screen-hud[data-hud-state="gameover"] .hud-admin {
  display: none;
}
.hud-pause-btn {
  display: none;
}
/* display:grid, no flex: .iconbtn ya es display:grid;place-items:center
   para centrar el ícono — flex pisaba ese display y con eso
   place-items dejaba de centrar en el eje horizontal (justify-items no
   existe en flexbox), el ícono quedaba pegado a la izquierda del botón. */
.screen-hud[data-hud-state="playing"] .hud-pause-btn {
  display: grid;
}

/* Chip de stat: etiqueta arriba (Condensed, tracking, --ink-3), número
   abajo (Fredoka, tabular-nums para que el ancho no tiemble al cambiar
   de dígitos — es justo el detalle que delataba al HUD viejo). */
.hud-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 5px 14px;
  border-radius: var(--r-md);
  min-width: 62px;
}
.hud-stat .k {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 9px;
  color: var(--ink-3);
}
.hud-stat .v {
  font-family: var(--display);
  font-weight: 600;
  font-size: 16px;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: #fff;
}
.hud-stat.height .v {
  color: var(--green);
}
.hud-stat.record .v,
.hud-stat.timer .v {
  color: var(--gold);
}
.hud-stat.timer.low .v {
  color: var(--alert);
}
/* Modo Marea — "Distancia a la marea". Base cian (a salvo), ámbar en
   advertencia (<=30 m), rojo con latido en peligro (<=12 m). */
.hud-stat.flood .v {
  color: #8fe9ff;
}
.hud-stat.flood.warn .v {
  color: #ff9a3d;
}
.hud-stat.flood.low .v {
  color: #ff5a5a;
  animation: hud-flood-pulse .7s ease-in-out infinite;
}
@keyframes hud-flood-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .45; }
}
@media (prefers-reduced-motion: reduce) {
  .hud-stat.flood.low .v { animation: none; }
}
.hud-stat.combo {
  display: none;
}
.hud-stat.combo.is-visible {
  display: flex;
}
.hud-stat.combo .v {
  color: #58d3e8;
}
.hud-stat.combo.flash {
  animation: hud-combo-flash .3s ease;
}
@keyframes hud-combo-flash {
  0% { transform: scale(1); }
  40% { transform: scale(1.18); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .hud-stat.combo.flash { animation: none; }
}

.hud-topleft {
  position: absolute;
  left: 14px;
  top: 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.hud-scorerow {
  display: flex;
  gap: 6px;
}

.hud-topcenter {
  position: absolute;
  left: 50%;
  top: 14px;
  transform: translateX(-50%);
  display: flex;
  gap: 6px;
}
.hud-topcenter .hud-stat {
  display: none;
}
.hud-topcenter .hud-stat.is-visible {
  display: flex;
}

/* Altura en vivo de los rivales en una carrera multijugador — zona
   PROPIA (no adentro de .hud-topcenter, que se oculta entera en touch
   más abajo). Debajo del récord/timer en escritorio para no pisarlos
   (esa fila es horizontal, mide una sola línea de alto); en touch,
   arriba del todo porque ahí .hud-topcenter ni existe. */
.hud-multiplayer {
  display: none;
  position: absolute;
  left: 50%;
  top: 62px;
  transform: translateX(-50%);
  flex-direction: column;
  align-items: center;
  gap: 3px;
}
.hud-multiplayer.is-visible {
  display: flex;
}
.hud-mp-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  background: rgba(8, 11, 20, .75);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  padding: 3px 10px;
  white-space: nowrap;
}
.hud-mp-row.is-finished {
  opacity: .6;
}
.hud-mp-label {
  white-space: nowrap;
}

/* Indicador de calidad de conexión estilo Minecraft: 5 barras ascendentes
   dibujadas con HTML/CSS (sin imágenes ni dependencias). El color sale del
   nivel 0–5 que calcula connectionLevelFor() a partir del RTT real medido
   por el canal (ping/pong), NO de navigator.connection. */
.hud-mp-bars {
  display: inline-flex;
  align-items: flex-end;
  gap: 1.5px;
  height: 11px;
  flex: none;
}
.hud-mp-bars i {
  width: 3px;
  background: rgba(255, 255, 255, .16);
  border-radius: .5px;
}
.hud-mp-bars i:nth-child(1) { height: 40%; }
.hud-mp-bars i:nth-child(2) { height: 55%; }
.hud-mp-bars i:nth-child(3) { height: 70%; }
.hud-mp-bars i:nth-child(4) { height: 85%; }
.hud-mp-bars i:nth-child(5) { height: 100%; }
.hud-mp-bars.lvl-unknown i { background: rgba(255, 255, 255, .24); }
.hud-mp-bars.lvl-0 i { background: rgba(255, 255, 255, .12); }
.hud-mp-bars.lvl-1 i.on,
.hud-mp-bars.lvl-2 i.on { background: #ff6b3d; }
.hud-mp-bars.lvl-3 i.on { background: #ffd95a; }
.hud-mp-bars.lvl-4 i.on,
.hud-mp-bars.lvl-5 i.on { background: #5fdb6b; }

[data-platform="touch"] .hud-multiplayer {
  top: 44px;
}
[data-platform="touch"] .hud-mp-row {
  gap: 5px;
  font-size: 9px;
  padding: 3px 8px;
}
[data-platform="touch"] .hud-mp-bars {
  height: 10px;
}

.hud-topright {
  position: absolute;
  right: 14px;
  top: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Píldora de volumen: mismo vidrio que el resto, track+thumb finitos en
   vez del control ancho de antes — nada de contorno blanco grueso. */
.hud-volume {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 32px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  width: 84px;
}
.hud-volume svg {
  width: 13px;
  height: 13px;
  color: var(--ink-2);
  flex: none;
  display: block;
}
.hud-volume .track {
  position: relative;
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .18);
}
.hud-volume .fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: 2px;
  background: var(--gold);
}
.hud-volume .thumb {
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--gold);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 2px rgba(5, 7, 14, .9);
}

/* Vidas: los 5 corazones de siempre, no un "x3/5" — se leen de un
   vistazo. El perdido no desaparece (eso "salta"), se apaga: mismo trazo
   al 18% de opacidad. */
.hud-lives {
  display: flex;
  gap: 3px;
}
.hud-heart {
  position: relative;
}
.hud-heart svg {
  width: 15px;
  height: 15px;
  display: block;
}
/* hh-base: silueta siempre presente al 18% (el corazón "vacío"). hh-fill: el
   relleno rojo por encima, recortable desde arriba con --drain (0 lleno, 1
   vacío) para el ahogo del modo Marea. */
.hud-heart .hh-base {
  fill: var(--alert);
  opacity: .18;
}
.hud-heart .hh-fill {
  fill: var(--alert);
  transition: clip-path .12s linear, opacity .15s ease, fill .15s ease;
  clip-path: inset(calc(var(--drain, 0) * 100%) 0 0 0);
}
.hud-heart.lost .hh-fill {
  opacity: 0;
}
.hud-heart.draining .hh-fill {
  fill: #8fb6ff;
}
.hud-heart.draining {
  animation: hud-heart-drown .7s ease-in-out infinite;
}
.hud-heart.pulse {
  animation: hud-heart-pulse .4s ease;
}
@keyframes hud-heart-pulse {
  0% { transform: scale(1); }
  35% { transform: scale(1.55); }
  100% { transform: scale(1); }
}
@keyframes hud-heart-drown {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  .hud-heart.pulse,
  .hud-heart.draining { animation: none; }
}

/* Power-ups: un solo idioma visual para los 8 — la forma del ícono los
   distingue, no un anillo de color saturado por cada uno (eso era el
   "arcoíris" que rompía la regla del dorado como única voz). */
.hud-powerups {
  position: absolute;
  left: 14px;
  bottom: 14px;
  display: flex;
  gap: 8px;
}
.pu-tile {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  /* Se tocan en plena carrera con el otro dedo sosteniendo el joystick: no
     dejar que el navegador interprete gestos (ver .duel-hud en duel.css). */
  touch-action: none;
}
.pu-tile .ic svg {
  width: 19px;
  height: 19px;
  color: var(--ink);
  opacity: .55;
  display: block;
  transition: opacity .15s ease, color .15s ease;
}
.pu-tile.locked .ic svg {
  opacity: .28;
}
/* .on: power-up activo AHORA MISMO (no solo en stock) — borde dorado +
   arco de progreso (.pu-ring) alrededor del tile marcando el tiempo que
   le queda. Es el único momento en que el ícono se pone dorado. */
.pu-tile.on {
  border-color: rgba(255, 217, 90, .6);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .45), inset 0 1px 0 var(--inner-hi), 0 0 0 1px rgba(255, 217, 90, .35);
}
.pu-tile.on .ic svg {
  opacity: 1;
  color: var(--gold);
}
.pu-ring {
  position: absolute;
  inset: 0;
  transform: rotate(-90deg);
  display: none;
  pointer-events: none;
}
.pu-tile.on .pu-ring {
  display: block;
}
.pu-ring circle {
  fill: none;
  stroke: var(--gold);
  stroke-width: 2.5px;
  stroke-linecap: round;
  /* Circunferencia de un círculo r=18 (2*PI*18) — el radio vive como
     PU_RING_R en main.js, junto a donde se arma este mismo <svg>. Si
     ese número cambia algún día, este también tiene que cambiar. Solo
     stroke-dashoffset se toca desde JS (ver syncHUD), por eso el resto
     del dibujo del arco vive acá. */
  stroke-dasharray: 113.1;
  stroke-dashoffset: 113.1;
}
/* Por agotarse (últimos ~2s de un power-up activo): el arco parpadea en
   vez de solo achicarse — mucho más notorio de reojo en plena partida. */
.pu-tile.blink .pu-ring circle {
  animation: pu-blink .5s steps(1) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .pu-tile.blink .pu-ring circle { animation: none; }
}
@keyframes pu-blink {
  50% { opacity: .15; }
}
.pu-meta {
  position: absolute;
  right: -4px;
  bottom: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  background: var(--ground-2);
  border: 1px solid var(--hairline);
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-size: 9px;
  color: var(--ink-2);
}

/* right-bottom: mismo tratamiento que .title-stats (etiqueta arriba,
   número Fredoka abajo) para el "Personal", más el chip de ADMIN — LOS
   DOS adentro de la misma columna flex, en vez de cada uno con su propio
   bottom:Npx adivinado. Un bottom fijo asumía una altura fija del bloque
   de Personal (dos filas + su gap) que en la práctica es más alta de lo
   que parecía — con flex+gap ya no hace falta acertarle a ese número:
   si alguno de los dos cambia de alto, el otro se corre solo, nunca se
   pisan. */
.hud-bottomright {
  position: absolute;
  right: 14px;
  bottom: 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
.hud-bottomright .stats {
  text-align: right;
}
.hud-bottomright .v {
  font-size: 18px;
}

/* Chip de admin: discreto — el rojo/dorado quedan afuera a propósito,
   este es el único lugar con acento verde, y solo aparece si
   admin.isModified() es real. */
.hud-admin {
  display: none;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  border-radius: var(--r-pill);
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 9px;
  color: var(--green);
}
.hud-admin.is-visible {
  display: inline-flex;
}
.hud-admin::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 6px var(--green);
}

/* ---------------- variante celular: HUD compacto y despejado ----------
   En una pantalla táctil el juego necesita más superficie libre que los
   menús. Los datos siguen siendo legibles, pero dejan de usar la receta
   agrandada que convertía cada stat en una alerta y hacía que los ocho
   power-ups ni siquiera entraran completos en vertical. */
[data-platform="touch"] .hud-topcenter {
  top: 8px;
}
/* El récord se omite en celular para no recargar el centro, pero el
   cronómetro de Contrarreloj es información indispensable para jugar.
   Antes se ocultaba el contenedor entero y los 60 segundos quedaban
   invisibles aunque la lógica siguiera descontándolos. */
[data-platform="touch"] .hud-topcenter .hud-stat.record {
  display: none;
}
[data-platform="touch"] .hud-topleft {
  left: 8px;
  top: 8px;
  gap: 4px;
}
[data-platform="touch"] .hud-scorerow {
  gap: 4px;
}
[data-platform="touch"] .hud-powerups {
  left: 50%;
  right: auto;
  bottom: 7px;
  top: auto;
  transform: translateX(-50%);
  display: flex;
  flex-direction: row;
  /* 10px y no 4: es lo que necesita el área táctil ampliada de .pu-tile::after
     (5px por lado) para no pisar la de la ficha de al lado. */
  gap: 10px;
}
/* Con el HUD de duelo en pantalla, la fila deja de centrarse a lo ancho y se
   ancla a la izquierda: el duelo ocupa la esquina de abajo a la derecha y las
   dos zonas táctiles quedaban una encima de la otra justo donde va el pulgar
   derecho — un toque ahí podía disparar el poder equivocado. Con las fichas
   más grandes de arriba el solapamiento era peor, así que se separan. wrap
   para que, en el caso raro de tener muchas a la vez, suban a otra fila en
   vez de meterse abajo del duelo. */
[data-platform="touch"] .screen-hud:has(.duel-hud:not([hidden])) .hud-powerups {
  left: 8px;
  right: auto;
  transform: none;
  max-width: 54%;
  flex-wrap: wrap;
}
/* Nada de HUD en las esquinas de abajo en celular — ahí van los
   pulgares. El personal/admin no están en la lista de lo que se
   necesita ver mientras se trepa (records grandes ya se ven en la
   pantalla de resultado). */
[data-platform="touch"] .hud-bottomright,
[data-platform="touch"] .hud-admin {
  display: none;
}
[data-platform="touch"] .hud-stat {
  min-width: 54px;
  padding: 4px 9px;
  border-radius: 10px;
}
[data-platform="touch"] .hud-stat .k {
  font-size: 8px;
  letter-spacing: .11em;
}
[data-platform="touch"] .hud-stat .v {
  font-size: 15px;
}
[data-platform="touch"] .hud-stat.combo.is-visible {
  align-self: flex-start;
  flex-direction: row;
  align-items: center;
  gap: 5px;
  min-width: 0;
  padding: 3px 8px;
}
[data-platform="touch"] .hud-topright {
  right: 8px;
  top: 8px;
  gap: 4px;
}
[data-platform="touch"] .hud-topright .iconbtn {
  width: 28px;
  height: 28px;
  border-radius: 9px;
}
[data-platform="touch"] .hud-topright .iconbtn svg {
  width: 14px;
  height: 14px;
}
[data-platform="touch"] .hud-volume {
  width: 64px;
  height: 28px;
  gap: 5px;
  padding: 0 8px;
}
[data-platform="touch"] .hud-volume svg {
  width: 11px;
  height: 11px;
}
[data-platform="touch"] .hud-volume .thumb {
  width: 7px;
  height: 7px;
}
/* Se tocan en plena carrera, con el otro dedo sosteniendo el joystick: si se
   les erra, el toque cae en el canvas. Todo acá está en px de MAQUETA y #ui
   se escala por --ui-scale (alto del canvas / 370, ver updateUIStage en
   viewport.js), así que el tamaño real depende de la pantalla. La más chica
   que se soporta es 320 px de alto => escala 0,865. Con los 32 px de antes
   quedaban 27,7 px reales, muy por debajo de los 44 que el propio panel de
   admin ya exige. 40 px + el área ampliada de abajo llega a 43,3 px reales
   ahí, y a 50+ en un celular normal. */
[data-platform="touch"] .pu-tile {
  width: 40px;
  height: 40px;
  border-radius: 11px;
}
/* Amplía SOLO el área que recibe el toque, sin mover ni agrandar el dibujo.
   El pseudo-elemento es parte del propio .pu-tile para el hit-test, así que
   no hace falta tocar el JS. 5px por lado => 50px de maqueta. El gap de la
   fila es 10px (ver .hud-powerups de abajo): 5+5 = 10, o sea que dos fichas
   vecinas se tocan justo sin pisarse — si se superpusieran, un toque en el
   borde activaría el power-up equivocado. */
[data-platform="touch"] .pu-tile::after {
  content: '';
  position: absolute;
  inset: -5px;
}
[data-platform="touch"] .pu-tile .ic svg {
  width: 19px;
  height: 19px;
}
[data-platform="touch"] .pu-meta {
  right: -3px;
  bottom: -3px;
  min-width: 13px;
  height: 13px;
  padding: 0 3px;
  font-size: 7.5px;
}
[data-platform="touch"] .hud-heart svg {
  width: 14px;
  height: 14px;
}
[data-platform="touch"] .hud-lives {
  gap: 2px;
}

/* ===================== Notificaciones con canales =====================
   Reemplaza cuatro sistemas que no se conocían entre sí (showToast/
   drawToast, drawMilestoneBanner, drawBiomeAlert — todos canvas, cuatro
   tipografías/timings/criterios de posición distintos) por notify() y
   tres canales, cada uno con su zona propia dentro del mismo espacio
   escalado de #ui — ninguno invade la zona de otro ni la del HUD:
     - urgente: centro, justo debajo del HUD.
     - progreso: esquina superior derecha en PC, superior izquierda en
       celular (lejos del pulgar y del HUD).
     - ambiente: arriba a la izquierda, discreto, debajo de la columna de
       puntos/altura/vidas/combo del HUD.
   .screen-notify se oculta entera en INTRO (créditos) — en cualquier
   otro estado, TÍTULO incluido, queda visible: el aviso "shh, secreto"
   del easter egg de la luna dispara con game.state===TITLE. */
.screen-notify {
  position: absolute;
  inset: 0;
  display: none;
}
.screen-notify.is-visible {
  display: block;
}

/* Receta compartida de tarjeta (urgente/progreso — ambiente es más chico
   y sin vidrio, ver más abajo). glass ya pone el fondo/borde/blur; acá
   solo el layout interno. */
.notify-card {
  display: flex;
  align-items: center;
  gap: 10px;
  border-radius: var(--r-md);
}
.notify-card .ic svg {
  width: 20px;
  height: 20px;
  display: block;
  flex: none;
}
.notify-card .text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.notify-card .title {
  font-family: var(--display);
  font-weight: 600;
  font-size: 14px;
  line-height: 1.2;
  color: var(--ink);
}
.notify-card .detail {
  font-family: var(--body);
  font-size: 11.5px;
  line-height: 1.25;
  color: var(--ink-3);
}
.notify-card .count {
  font-family: var(--display);
  font-weight: 600;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--gold);
  flex: none;
}
.notify-card .count:empty {
  display: none;
}

/* ---------------- urgente: centro, no se apila, shake + pulso ---------------- */
.notify-urgent {
  position: absolute;
  left: 50%;
  top: 65px;
  transform: translateX(-50%);
}
.notify-urgent-card {
  padding: 13px 22px;
  min-width: 260px;
  justify-content: center;
  border-color: rgba(255, 255, 255, .18);
}
.notify-urgent-card .title {
  font-size: 15px;
}
.notify-urgent-card.is-in {
  animation: notify-urgent-in .18s cubic-bezier(.34, 1.56, .64, 1) both;
}
@keyframes notify-urgent-in {
  0% { opacity: 0; transform: scale(.85); }
  100% { opacity: 1; transform: scale(1); }
}
.notify-urgent-card.is-leaving {
  opacity: 0;
  transform: scale(.95);
  transition: opacity .25s ease, transform .25s ease;
}
.notify-urgent-card.tone-danger {
  border-color: rgba(230, 63, 45, .5);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .45), inset 0 1px 0 var(--inner-hi), 0 0 26px rgba(230, 63, 45, .25);
}
.notify-urgent-card.tone-danger .ic svg,
.notify-urgent-card.tone-danger .count {
  color: var(--alert);
}
.notify-urgent-card.tone-success {
  border-color: rgba(111, 224, 138, .5);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .45), inset 0 1px 0 var(--inner-hi), 0 0 26px rgba(111, 224, 138, .25);
}
.notify-urgent-card.tone-success .ic svg,
.notify-urgent-card.tone-success .count {
  color: var(--green);
}

/* ---------------- progreso: cola real, máximo 3, desliza desde afuera ---------------- */
.notify-progress {
  position: absolute;
  right: 14px;
  top: 60px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-end;
}
/* Sin vidrio (el nodo ya no lleva la clase .glass, ver syncProgressNotify
   en main.js): el canal de progreso informa, no interrumpe, y una tarjeta
   opaca de 250px pegada al borde tapaba juego por avisos que no piden
   nada. Queda la franja de color como único ancla visual, el texto más
   chico y una sombra dura que lo mantiene legible sobre cualquier fondo.
   El ancho pasa a ser el del contenido (max-width como tope) en vez de una
   caja fija: sin fondo, una caja fija solo agregaba aire vacío. */
.notify-progress-card {
  width: auto;
  max-width: 230px;
  padding: 2px 0 2px 7px;
  border-left: 2px solid var(--gold);
  text-shadow: 0 1px 4px rgba(0, 0, 0, .95), 0 0 2px rgba(0, 0, 0, .8);
  opacity: 0;
  transform: translateX(24px);
  transition: opacity .18s ease, transform .18s ease;
}
.notify-progress-card .ic svg {
  width: 15px;
  height: 15px;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .9));
}
.notify-progress-card .title {
  font-size: 12px;
}
.notify-progress-card .detail {
  font-size: 10.5px;
  color: var(--ink-2);
}
.notify-progress-card.is-in {
  opacity: 1;
  transform: translateX(0);
}
.notify-progress-card.is-leaving {
  opacity: 0;
  transition-duration: .25s;
}
.notify-progress-card.tone-muted {
  border-left-color: var(--ink-3);
}
.notify-progress-card.tone-error {
  border-left-color: var(--alert);
}
.notify-progress-card.tone-error .count {
  color: var(--alert);
}

/* ---------------- ambiente: discreto, mudo, nunca interrumpe ---------------- */
.notify-ambient {
  position: absolute;
  left: 14px;
  top: 130px;
}
.notify-ambient-card {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px 6px 8px;
  border-radius: var(--r-sm);
  opacity: 0;
  transition: opacity .18s ease;
}
.notify-ambient-card.is-in {
  opacity: 1;
}
.notify-ambient-card.is-leaving {
  opacity: 0;
  transition-duration: .25s;
}
.notify-ambient-card .stripe {
  width: 4px;
  align-self: stretch;
  border-radius: 2px;
}
.notify-ambient-card .text {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .16em;
  font-size: 12px;
  color: var(--ink);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .notify-urgent-card { animation: none; transform: none; }
  .notify-urgent-card.is-in { animation: none; transform: none; }
  .notify-urgent-card.is-leaving { transform: none; }
  .notify-progress-card { transform: none; }
  .notify-progress-card.is-in { transform: none; }
  .notify-progress-card.is-leaving { transform: none; }
}

/* ---------------- variante celular: avisos breves, de una línea --------
   El tamaño compacto se aplica a TODO celular, no solo a los de poca
   altura. Así un equipo táctil de alta resolución no termina escalando
   tarjetas de escritorio que tapan al mono o los obstáculos. */
[data-platform="touch"] .notify-card.glass {
  background: rgba(8, 11, 20, .74);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .32), inset 0 1px 0 var(--inner-hi);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}
[data-platform="touch"] .notify-urgent {
  top: 72px;
}
[data-platform="touch"] .notify-urgent-card {
  min-width: 0;
  max-width: 170px;
  padding: 5px 10px;
  gap: 6px;
}
[data-platform="touch"] .notify-urgent-card .ic svg {
  width: 13px;
  height: 13px;
}
[data-platform="touch"] .notify-urgent-card .title {
  font-size: 10.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
[data-platform="touch"] .notify-urgent-card .detail {
  display: none;
}
[data-platform="touch"] .notify-progress {
  left: 8px;
  right: auto;
  top: 108px;
  gap: 5px;
  align-items: flex-start;
}
[data-platform="touch"] .notify-progress-card {
  width: auto;
  max-width: 150px;
  padding: 1px 0 1px 5px;
  gap: 5px;
  border-left-width: 2px;
  transform: translateX(-16px);
}
[data-platform="touch"] .notify-progress-card.is-in {
  transform: translateX(0);
}
[data-platform="touch"] .notify-progress-card .ic svg {
  width: 13px;
  height: 13px;
}
[data-platform="touch"] .notify-progress-card .title {
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
[data-platform="touch"] .notify-progress-card .detail {
  display: none;
}
[data-platform="touch"] .notify-progress-card .count {
  font-size: 10px;
}
[data-platform="touch"] .notify-ambient {
  left: 8px;
  top: 92px;
}
[data-platform="touch"] .notify-ambient-card {
  gap: 5px;
  padding: 3px 8px 3px 5px;
}
[data-platform="touch"] .notify-ambient-card .stripe {
  width: 3px;
}
[data-platform="touch"] .notify-ambient-card .text {
  max-width: 145px;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 9px;
}

/* ===================== Variante compacta (celular bajo) =====================
   --ui-scale resuelve bien las pantallas grandes (todo crece o se achica
   parejo) pero no alcanza cuando hay POCA ALTURA: ahí no hay que escalar
   todo por igual, hay que cambiar la receta (menos aire, tipografía más
   chica, JUGAR angosto en vez de ancho). :root.is-compact lo pone
   updateCompactMode() en viewport.js cuando el alto útil real (viewport
   menos las safe areas verticales, no el @media (max-height) crudo, que
   no sabe restar el notch) baja de ~420px — el caso típico es un celular
   en horizontal con poca altura física (iPhone SE/14/Pro Max
   horizontal). Solo aplica en touch: en PC nunca hay tan poca altura
   real disponible como para necesitar esto. */
/* Ya no hay top: que pisar acá — con el layout de columna flex (arriba)
   el acomodo vertical sale solo del padding-top/gap; compacto solo
   necesita apretar esos dos números y achicar tipografía/tamaños. */
:root.is-compact[data-platform="touch"] .screen-title.is-visible {
  padding-top: 8px;
}
:root.is-compact[data-platform="touch"] .title-logo {
  /* 25% más chico que el touch normal (36px). */
  font-size: 27px;
}
:root.is-compact[data-platform="touch"] .title-tagline {
  margin-top: 4px;
  font-size: 8px;
}
:root.is-compact[data-platform="touch"] .title-central {
  gap: 8px;
  padding: 2px 16px 14px;
}
:root.is-compact .title-modes .mode {
  padding: 4px 7px;
  font-size: 9.5px;
  letter-spacing: .04em;
}
:root.is-compact[data-platform="touch"] .title-cta {
  height: 52px;
  /* 34% del ancho de pantalla en vez de un ancho fijo — hoy se comía
     casi la mitad. Como #ui/.screen-title ya están en el mismo espacio
     escalado que el resto, un % acá corresponde al mismo % de la
     pantalla real. */
  width: 34%;
  min-width: 200px;
}
:root.is-compact .title-navbar {
  height: 56px;
}
:root.is-compact .title-navbar .tile {
  width: 84px;
  /* Nunca por debajo de 44px de lado tocable en la vida real — con
     --ui-scale rondando ~1 en estos altos (370 de referencia contra una
     pantalla de ~370-410px de alto útil), 44px de maqueta y 44px reales
     quedan lo bastante cerca como para no necesitar una conversión
     aparte acá. */
  height: 44px;
}
:root.is-compact .title-navbar .tile .ic svg {
  width: 15px;
  height: 15px;
}
:root.is-compact .title-navbar .tile .lb {
  font-size: 9px;
}

/* Alertas en celular bajo: la receta de escritorio escalada (--ui-scale)
   ya no alcanza acá — una tarjeta pensada para 1000×476 sigue siendo
   "media pantalla" de alto en un stage de 370, por más que se escale
   parejo. Tope de espacio: todo lo que se muestre a la vez, junto, no
   puede pasar del 18% del alto de pantalla — con esto abajo, urgente
   (~28px), 2 tarjetas de progreso (~30px c/u) y ambiente (~14px) quedan
   bien por debajo de esa cota incluso si las tres suenan juntas, porque
   viven en zonas distintas (no se apilan entre sí). Nada de esto se
   centra sobre el carril central por donde caen las rocas ni cruza el
   ancho completo — eso queda para el HUD nomás. */
:root.is-compact[data-platform="touch"] .notify-urgent-card {
  min-width: 0;
  max-width: 155px;
  padding: 4px 8px;
  gap: 5px;
}
:root.is-compact[data-platform="touch"] .notify-urgent-card .ic svg {
  width: 12px;
  height: 12px;
}
:root.is-compact[data-platform="touch"] .notify-urgent-card .title {
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
:root.is-compact[data-platform="touch"] .notify-urgent-card .detail {
  display: none;
}
/* Máximo 2 en vez de 3 (ver notifyMaxVisible() en main.js — mismo
   :root.is-compact que acá, un solo criterio de "pantalla chica"),
   tarjetas al ~70% del tamaño de escritorio y una sola línea: sin el
   detalle, y el título se corta con puntos suspensivos en vez de
   agrandar la tarjeta o partirse en dos renglones. */
:root.is-compact[data-platform="touch"] .notify-progress {
  gap: 4px;
}
:root.is-compact[data-platform="touch"] .notify-progress-card {
  width: 140px;
  padding: 4px 6px;
  gap: 5px;
}
:root.is-compact[data-platform="touch"] .notify-progress-card .ic svg {
  width: 12px;
  height: 12px;
}
:root.is-compact[data-platform="touch"] .notify-progress-card .title {
  font-size: 9.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
:root.is-compact[data-platform="touch"] .notify-progress-card .detail {
  display: none;
}
:root.is-compact[data-platform="touch"] .notify-progress-card .count {
  font-size: 9.5px;
}
/* El canal ambiente ya es una sola línea sin caja — alcanza con achicarlo
   un poco más, no necesita una receta nueva. */
:root.is-compact[data-platform="touch"] .notify-ambient-card {
  padding: 3px 7px 3px 5px;
  gap: 5px;
}
:root.is-compact[data-platform="touch"] .notify-ambient-card .text {
  font-size: 8.5px;
}

/* ===================== Pausa =====================
   Mismo sistema que el resto: tarjeta de vidrio que se ajusta a su
   contenido (no un cartel enorme para dos líneas), botones ADENTRO
   (el .cta dorado de la portada + las mismas .row de su menú), scrim
   real detrás en vez del juego a pleno brillo. */
.screen-pause {
  position: absolute;
  inset: 0;
  display: none;
}
.screen-pause.is-visible {
  display: block;
}
.pause-scrim {
  position: absolute;
  inset: 0;
  background: rgba(3, 5, 11, .68);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.pause-card {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 420px;
  max-width: 92%;
  padding: 22px 22px 16px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  border-radius: var(--r-lg);
}
.pause-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 26px;
  color: var(--ink);
  text-align: center;
}
.pause-live-note {
  margin: -4px 0 2px;
  color: #ffc7bd;
  font: 600 12px/1.4 var(--body);
  text-align: center;
}
.pause-stats {
  display: flex;
  justify-content: center;
  gap: 8px;
}
.pause-stats .hud-stat {
  align-items: center;
  text-align: center;
}
.pause-cta {
  width: 100%;
  height: 54px;
  justify-content: center;
}
.pause-menu {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pause-keys {
  text-align: center;
  font-family: var(--body);
  font-size: 11px;
  color: var(--ink-3);
}
.pause-keys kbd {
  font-family: var(--body);
  font-size: 10px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 1px 6px;
  color: var(--ink-2);
  margin: 0 2px;
}
.pause-keys-touch {
  display: none;
}
[data-platform="touch"] .pause-keys-desktop {
  display: none;
}
[data-platform="touch"] .pause-keys-touch {
  display: block;
}
/* En touch/compacto hay menos alto para regalar — achicar título y
   apretar el padding en vez de arriesgar que la tarjeta no entre. */
[data-platform="touch"] .pause-card {
  padding: 16px 18px 12px;
  gap: 9px;
}
[data-platform="touch"] .pause-title {
  font-size: 20px;
}
[data-platform="touch"] .pause-cta {
  height: 46px;
}
:root.is-compact .pause-card {
  gap: 7px;
  padding: 12px 16px 10px;
}
:root.is-compact .pause-title {
  font-size: 17px;
}
:root.is-compact .pause-cta {
  height: 42px;
}
:root.is-compact .pause-stats .hud-stat {
  padding: 3px 10px;
}

/* ===================== PIN de admin + editor de desafíos =====================
   Los únicos dos lugares del panel de admin con texto/número tipeado de
   verdad — reusan el mismo esqueleto que .screen-pause (scrim + tarjeta
   de vidrio centrada), con inputs nativos en vez de pelear con canvas.
   .screen-pin sirve para las dos pantallas (buildPinPromptUI y
   buildChallengeEditorUI en main.js arman una .screen-pin cada una). */
.screen-pin {
  position: absolute;
  inset: 0;
  display: none;
}
.screen-pin.is-visible {
  display: block;
}
/* El PIN del ranking se abre encima de un canvas grande. Dos filtros blur
   simultáneos (scrim + tarjeta .glass) hacían recomponer toda la pantalla y
   producían tirones incluso en PC. Mantiene la misma oscuridad/estética sin
   filtros de fondo; el editor de desafíos conserva su tratamiento original. */
.admin-pin-scrim {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.admin-pin-card.glass {
  background: linear-gradient(180deg, rgba(22, 27, 43, .99), rgba(8, 11, 20, .99));
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: 0 8px 18px rgba(0, 0, 0, .42), inset 0 1px 0 var(--inner-hi);
}
.pin-card {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 320px;
  max-width: 90%;
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  border-radius: var(--r-lg);
}
.pin-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 18px;
  color: var(--ink);
  text-align: center;
}
.pin-msg {
  font-family: var(--body);
  font-size: 11.5px;
  color: var(--ink-3);
  text-align: center;
  line-height: 1.3;
}
.pin-input,
.ce-input {
  width: 100%;
  box-sizing: border-box;
  background: rgba(5, 7, 14, .8);
  border: 1.5px solid var(--hairline);
  border-radius: 10px;
  color: var(--ink);
  font-family: var(--body);
  font-size: 15px;
  padding: 10px 12px;
  outline: none;
}
.pin-input:focus,
.ce-input:focus {
  border-color: var(--gold);
}
.pin-cta {
  width: 100%;
  height: 44px;
  justify-content: center;
}
.ce-card {
  width: 340px;
  gap: 6px;
}
.ce-label {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 9.5px;
  color: var(--ink-3);
  margin-top: 4px;
}
.ce-card .pin-cta {
  margin-top: 8px;
}

/* Selector de "Qué mide" (buildStatPicker en main.js). El <select> nativo
   listaba los 27 tipos planos y abría el desplegable gris del sistema, que
   no se puede estilar: este los agrupa, se filtra escribiendo y usa la
   paleta del juego. pointer-events acá porque el contenedor no lleva
   [data-interactive] — si lo llevara, tocar una opción encogería el
   desplegable entero (ver la regla [data-interactive]:active más arriba). */
.ce-picker {
  position: relative;
  pointer-events: auto;
}
.ce-picker-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: rgba(5, 7, 14, .8);
  border: 1.5px solid var(--hairline);
  border-radius: 10px;
  color: var(--ink);
  font-family: var(--body);
  font-size: 15px;
  padding: 10px 12px;
}
.ce-picker-btn:focus-visible,
.ce-picker.is-open .ce-picker-btn {
  border-color: var(--gold);
  outline: none;
}
.ce-picker-value {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ce-picker-caret {
  flex: none;
  font-size: 11px;
  color: var(--ink-3);
  transition: transform .15s ease;
}
.ce-picker.is-open .ce-picker-caret {
  transform: rotate(180deg);
}

.ce-picker-pop {
  display: none;
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  top: calc(100% + 6px);
  flex-direction: column;
  background: linear-gradient(180deg, rgba(22, 27, 43, .99), rgba(8, 11, 20, .99));
  border: 1.5px solid var(--hairline-hi);
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, .55);
  overflow: hidden;
}
.ce-picker.is-open .ce-picker-pop {
  display: flex;
}
/* Cuando no entra para abajo pero sí para arriba (ver open() en main.js). */
.ce-picker.opens-up .ce-picker-pop {
  top: auto;
  bottom: calc(100% + 6px);
}
/* Nada adentro del desplegable se encoge al tocarlo: la lista se movería
   debajo del dedo justo mientras estás eligiendo. */
.ce-picker-pop [data-interactive]:active {
  transform: none;
}

.ce-picker-search {
  width: 100%;
  box-sizing: border-box;
  background: rgba(5, 7, 14, .7);
  border: none;
  border-bottom: 1px solid var(--hairline);
  border-radius: 0;
  color: var(--ink);
  font-family: var(--body);
  font-size: 13px;
  padding: 9px 12px;
  outline: none;
}
.ce-picker-list {
  max-height: 232px;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Sin esto queda la barra blanca del sistema, que es lo único claro en
     toda la interfaz. Firefox por propiedad estándar, Chrome/Safari por el
     pseudo-elemento; los dos caminos pintan lo mismo. */
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .22) transparent;
}
.ce-picker-list::-webkit-scrollbar {
  width: 8px;
}
.ce-picker-list::-webkit-scrollbar-track {
  background: transparent;
}
.ce-picker-list::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .22);
  border-radius: 999px;
}
.ce-picker-list::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, .34);
}
.ce-picker-group {
  position: sticky;
  top: 0;
  background: rgba(10, 13, 22, .97);
  font-family: var(--label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 9px;
  color: var(--gold);
  padding: 7px 12px 4px;
}
.ce-picker-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  font-family: var(--body);
  font-size: 13px;
  line-height: 1.25;
  color: var(--ink-2);
}
.ce-picker-opt > span:first-child {
  flex: 1;
  min-width: 0;
}
.ce-picker-opt.is-active {
  background: rgba(255, 217, 90, .14);
  color: var(--ink);
}
.ce-picker-opt.is-chosen {
  color: var(--gold);
}
.ce-picker-off {
  flex: none;
  font-family: var(--label);
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--alert);
  border: 1px solid currentColor;
  border-radius: 4px;
  padding: 0 4px;
}
.ce-picker-empty {
  padding: 14px 12px;
  font-family: var(--body);
  font-size: 12px;
  color: var(--ink-3);
  text-align: center;
}

/* ===================== Lobby de multijugador =====================
   Mismo esqueleto que .screen-pin (scrim + tarjeta de vidrio centrada,
   ver buildLobbyUI en main.js) — acá adentro hay TRES sub-pantallas
   (.lobby-screen) apiladas en la misma tarjeta, una visible a la vez
   según game.multiplayer.uiPhase. */
.screen-lobby {
  position: absolute;
  inset: 0;
  display: none;
}
.screen-lobby.is-visible {
  display: block;
}
.lobby-card {
  width: 392px;
  max-height: calc(100% - 12px);
  overflow-y: auto;
  overscroll-behavior: contain;
  pointer-events: auto;
  gap: 0;
  padding: 18px 20px 16px;
}
.lobby-screen {
  display: none;
  flex-direction: column;
  gap: 10px;
}
.lobby-screen.is-visible {
  display: flex;
}
.lobby-screen .pin-title {
  font-size: 20px;
  letter-spacing: .01em;
}
.lobby-sub {
  margin: 0;
  font-family: var(--body);
  font-size: 12px;
  color: var(--ink-3);
  text-align: center;
  line-height: 1.35;
}

/* ---- código de sala ---- */
.lobby-code-input {
  text-align: center;
  letter-spacing: .3em;
  text-transform: uppercase;
  font-family: var(--display);
  font-size: 22px;
}
.lobby-code {
  align-self: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: 30px;
  letter-spacing: .2em;
  color: var(--gold);
  padding: 7px 20px 7px 24px;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, rgba(255, 217, 90, .12), rgba(255, 217, 90, .05));
  border: 1px solid rgba(255, 217, 90, .32);
  box-shadow: 0 0 22px rgba(255, 190, 60, .12), inset 0 1px 0 rgba(255, 255, 255, .08);
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.lobby-code:hover {
  border-color: rgba(255, 217, 90, .55);
  box-shadow: 0 0 26px rgba(255, 190, 60, .22), inset 0 1px 0 rgba(255, 255, 255, .1);
}
.lobby-copy-toast {
  align-self: center;
  height: 14px;
  margin-top: -6px;
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 11px;
  color: var(--gold);
  opacity: 0;
  transition: opacity .18s ease;
}
.lobby-copy-toast.is-in { opacity: 1; transition-duration: .18s; }
.lobby-copy-toast:not(.is-in) { transition-duration: .25s; }

/* ---- selector de modo (mismo lenguaje que .title-modes) ---- */
.lobby-mode-picker {
  display: grid;
  gap: 6px;
  margin-top: 2px;
}
.lobby-mode-label {
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 10px;
  color: var(--ink-3);
  text-align: center;
}
.lobby-mode-seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 4px;
  border-radius: var(--r-md);
  background: rgba(5, 7, 14, .55);
  border: 1px solid var(--hairline);
}
.lobby-mode-chip {
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  cursor: pointer;
  min-height: 32px;
  padding: 5px 6px;
  border-radius: var(--r-sm);
  background: transparent;
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: 11.5px;
  color: var(--ink-3);
  transition: background .14s ease, color .14s ease, box-shadow .14s ease;
}
@media (hover: hover) {
  .lobby-mode-chip:not(.on):hover { color: var(--ink); background: rgba(255, 255, 255, .05); }
}
.lobby-mode-chip.on {
  background: linear-gradient(180deg, var(--gold), var(--gold-deep));
  color: var(--gold-ink);
  font-weight: 700;
  box-shadow: 0 2px 8px rgba(255, 190, 40, .3);
}
.lobby-mode-chip.on[data-mode="flood"] {
  background: linear-gradient(180deg, #c9a6ff, #9a63ff);
  color: #180a2c;
  box-shadow: 0 2px 10px rgba(150, 90, 255, .35);
}
.lobby-mode-seg.is-disabled {
  opacity: .5;
  filter: saturate(.5);
  pointer-events: none;
}
.lobby-mode-rules {
  margin: 0;
  text-align: center;
  font-family: var(--body);
  font-size: 12px;
  line-height: 1.35;
  color: var(--ink-2);
}
.lobby-mode-rules[data-mode="flood"] { color: #c9a6ff; }
.lobby-mode-help {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-family: var(--body);
  font-size: 11.5px;
  line-height: 1.3;
  color: var(--ink-3);
  text-align: center;
}
.lobby-mode-help::before {
  content: "";
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 7px currentColor;
}
.lobby-mode-help.is-agreed { color: var(--green); }

/* ---- lista de jugadores ---- */
.lobby-players {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.lobby-player-row {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 36px;
  padding: 0 12px;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, rgba(255, 255, 255, .055), rgba(255, 255, 255, .02));
  border: 1px solid var(--hairline);
  font-family: var(--body);
  font-size: 13px;
  color: var(--ink);
}
.lobby-player-row.is-me {
  border-color: rgba(255, 217, 90, .35);
  background: linear-gradient(180deg, rgba(255, 217, 90, .09), rgba(255, 217, 90, .02));
}
.lobby-player-row.is-ready {
  border-color: rgba(111, 224, 138, .5);
}
.lobby-player-row.is-spectator {
  opacity: .62;
}
.lobby-player-row .lb {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lobby-player-dot {
  flex-shrink: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ink-3);
  box-shadow: 0 0 6px rgba(0, 0, 0, .4);
}
.lobby-player-dot[data-seat="0"] { background: var(--gold); box-shadow: 0 0 7px rgba(255, 200, 60, .5); }
.lobby-player-dot[data-seat="1"] { background: #58d3e8; box-shadow: 0 0 7px rgba(88, 211, 232, .5); }
.lobby-player-dot[data-seat="2"] { background: #c65adc; box-shadow: 0 0 7px rgba(198, 90, 220, .5); }
.lobby-player-row.is-spectator .lobby-player-dot { background: var(--ink-3); box-shadow: none; }
.lobby-mode-vote {
  flex-shrink: 0;
  padding: 3px 8px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--hairline);
  color: var(--ink-2);
  font-family: var(--label);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 10.5px;
  line-height: 1.3;
}
.lobby-mode-vote[data-mode="flood"] {
  color: #c9a6ff;
  border-color: rgba(170, 110, 255, .4);
  background: rgba(150, 90, 255, .1);
}
.lobby-mode-vote.is-agreed {
  color: var(--green);
  border-color: rgba(111, 224, 138, .45);
  background: rgba(111, 224, 138, .1);
}
.lobby-ready-dot {
  flex-shrink: 0;
  color: var(--green);
  font-weight: 700;
  width: 14px;
  text-align: center;
}

/* ---- botón LISTO + cuenta regresiva ---- */
.lobby-ready-control {
  margin-top: 2px;
}
.lobby-ready-control b {
  font-size: 20px;
  white-space: nowrap;
}
.lobby-ready-control[aria-disabled="true"] {
  opacity: .5;
  filter: saturate(.5);
  cursor: not-allowed;
}
.lobby-countdown {
  display: none;
  align-self: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: 46px;
  color: var(--gold);
  text-shadow: 0 0 26px rgba(255, 190, 60, .35);
  padding: 6px 0;
  animation: lobby-countdown-beat .9s ease-in-out infinite;
}
.lobby-countdown.is-visible {
  display: block;
}
@keyframes lobby-countdown-beat {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.09); }
}
@media (prefers-reduced-motion: reduce) {
  .lobby-countdown { animation: none; }
}
.is-hidden {
  display: none !important;
}
