/* ============ Reset + app shell ============ */
*,*::before,*::after{ box-sizing:border-box; }
html,body{ height:100%; margin:0; padding:0; }
html{ -webkit-text-size-adjust:100%; }
body{
  font-family:var(--f-ui);
  background:var(--bg-0);
  color:var(--tx-hi);
  overflow:hidden;
  overscroll-behavior:none;
  touch-action:manipulation;
  -webkit-tap-highlight-color:transparent;
  -webkit-user-select:none; user-select:none;
  font-weight:600;
  letter-spacing:.2px;
}
button{ font-family:inherit; color:inherit; border:0; background:none; cursor:pointer; }
.hidden{ display:none !important; }
img{ display:block; max-width:100%; }

/* Fondo animado: nebulosa sci-fi */
body::before{
  content:""; position:fixed; inset:-20%; z-index:-2;
  background:
    radial-gradient(60% 45% at 18% 12%, rgba(46,107,255,.28), transparent 62%),
    radial-gradient(55% 40% at 84% 22%, rgba(139,92,246,.24), transparent 64%),
    radial-gradient(70% 50% at 50% 106%, rgba(57,255,136,.14), transparent 60%),
    var(--bg-0);
  animation:nebula 26s ease-in-out infinite alternate;
}
@keyframes nebula{ 0%{transform:scale(1) translate(0,0)} 100%{transform:scale(1.12) translate(-2%,-2%)} }

/* Rejilla táctica sutil */
body::after{
  content:""; position:fixed; inset:0; z-index:-1; pointer-events:none; opacity:.22;
  background-image:
    linear-gradient(rgba(107,160,255,.10) 1px, transparent 1px),
    linear-gradient(90deg, rgba(107,160,255,.10) 1px, transparent 1px);
  background-size:44px 44px;
  mask-image:radial-gradient(circle at 50% 40%, #000 10%, transparent 78%);
  -webkit-mask-image:radial-gradient(circle at 50% 40%, #000 10%, transparent 78%);
}

/* ============ BOOT ============ */
.boot{
  position:fixed; top:0; bottom:0; z-index:9000;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:22px;
  background:radial-gradient(70% 55% at 50% 40%, #10173a 0%, #05070F 72%);
  transition:opacity var(--t-slow) var(--ease-out);
}
.boot.out{ opacity:0; pointer-events:none; }

/* =========================================================================
   CINEMÁTICA DE ARRANQUE
   Va por encima de la pantalla de carga (z 9500 > 9000) para que la carga
   siga trabajando por debajo mientras el vídeo se reproduce: cuando termina,
   el juego ya está listo y se entra sin espera añadida.
   ========================================================================= */
.intro{
  position:fixed; top:0; bottom:0; z-index:9500;
  background:#000;
  overflow:hidden;
  transition:opacity var(--t-slow) var(--ease-out);
}
.intro.out{ opacity:0; pointer-events:none; }

/* El vídeo se ancla a los cuatro lados del contenedor. Con `height:100%` a
   secas dentro de un grid la fila crecía hasta la altura intrínseca del vídeo
   (1560 px) y `100%` pasaba a medir eso: el vídeo se salía del marco por
   abajo. Anclado a `inset:0` no depende del tamaño del contenido.

   `contain` y no `cover`: el vídeo es 9:19.5 y el marco 9:16, así que `cover`
   recortaría por arriba y por abajo, justo donde está el logotipo. */
.intro-video{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:contain;
  background:#000;
  display:block;
  /* Zoom mínimo. Con `contain` la proporción del vídeo casi nunca cuadra con
     la de la pantalla y sobra una franja de uno o dos píxeles, que sobre los
     fotogramas en blanco se lee como una raya negra cruzando el pie de la
     imagen. Un 4 % la come sin recortar nada del logotipo. */
  transform:scale(1.04);
  transform-origin:center center;
}

.intro-skip{
  position:absolute; right:14px; bottom:calc(18px + var(--safe-b, 0px));
  padding:9px 16px; border-radius:99px;
  font-size:12px; font-weight:800; letter-spacing:.06em;
  color:#fff; background:rgba(8,11,22,.62);
  border:1px solid rgba(255,255,255,.26);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  /* Entra con retardo: si aparece en el fotograma 1 invita a saltarse la
     cinemática antes incluso de verla. */
  opacity:0; animation:introSkipIn .4s var(--ease-out) 1.1s forwards;
}
.intro-skip:active{ transform:scale(.94); }
@keyframes introSkipIn{ to{ opacity:1; } }

.intro-unmute{
  position:absolute; left:50%; transform:translateX(-50%);
  bottom:calc(64px + var(--safe-b, 0px));
  padding:8px 14px; border-radius:99px;
  font-size:11.5px; font-weight:700;
  color:#fff; background:rgba(8,11,22,.62);
  border:1px solid rgba(255,255,255,.22);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
}
.intro-unmute[hidden]{ display:none; }
.boot-logo{ text-align:center; }
/* El icono real de la aplicación, el mismo que se publica en la tienda. Antes
   había aquí un marcador de posición dibujado con CSS (cuadrado azul con un
   pentágono) que no se parecía en nada al icono definitivo.

   Se usa el PNG de 192 y no el de 512: a 82 px en pantalla, 192 ya da margen
   de sobra incluso en pantallas de densidad 2x, y pesa una fracción. Va en el
   precacheo del service worker, así que en la PWA aparece también sin
   conexión. */
.boot-logo-mark{
  display:block; width:82px; height:82px; margin:0 auto 14px;
  border-radius:22px;
  background:url("../icon-192.png") center / cover no-repeat, var(--bg-1);
  /* El icono es oscuro y el fondo de arranque también: sin un filo claro la
     pieza se difumina contra el fondo y no se lee como un icono. */
  border:1px solid rgba(139,146,255,.22);
  box-shadow:var(--glow-blue);
  animation:pulseMark 1.8s var(--ease-out) infinite;
}
@keyframes pulseMark{ 0%,100%{transform:scale(1)} 50%{transform:scale(1.07)} }
.boot-logo h1{
  margin:0; font-size:30px; line-height:1.02; letter-spacing:3px; font-weight:800;
  background:linear-gradient(180deg,#fff,#8FB4FF); -webkit-background-clip:text; background-clip:text; color:transparent;
}
.boot-logo em{ font-style:normal; color:var(--c-neon); -webkit-text-fill-color:var(--c-neon); text-shadow:var(--glow-neon); }
.boot-bar{ width:min(62vw,300px); height:6px; border-radius:99px; background:rgba(255,255,255,.09); overflow:hidden; }
.boot-bar i{ display:block; height:100%; width:0; background:var(--g-action); box-shadow:var(--glow-neon); transition:width .25s var(--ease-out); }
#boot-msg{ margin:0; font-size:12px; letter-spacing:2px; text-transform:uppercase; color:var(--tx-lo); }

/* =========================================================================
   ENCUADRE VERTICAL
   El juego es de móvil en vertical. `min()` devuelve el ancho real de la
   pantalla en un teléfono —así que ahí no cambia absolutamente nada— y en
   pantallas anchas (escritorio, tablet apaisada) encuadra la interfaz en un
   marco 9:16 centrado en lugar de estirarla: los botones se mantienen a
   distancia de pulgar y la composición del corredor no se deforma.
   ========================================================================= */
:root{ --stage-w: min(100vw, calc(100vh * 9 / 16)); }

.app, .mission, .boot, .intro, .login, .popup-layer, .toast-layer, .fx-layer{
  width:var(--stage-w);
  left:50%; right:auto;
  transform:translateX(-50%);
}
.app, .mission{
  box-shadow:0 0 0 1px rgba(139,146,255,.14), 0 0 90px rgba(0,0,0,.75);
}
.app.orbital-shell{background-image:var(--orbital-shell-bg);background-size:cover;background-position:center top;background-repeat:no-repeat}

/* ============ App layout ============ */
.app{
  position:fixed; top:0; bottom:0;
  display:grid;
  /* La columna se declara `minmax(0,1fr)`: si se deja implícita, el hijo con
     mayor contenido mínimo (la barra de navegación) estira la rejilla y todas
     las barras se salen del marco vertical. */
  grid-template-columns:minmax(0, 1fr);
  grid-template-rows:calc(var(--topbar-h) + var(--safe-t)) var(--subbar-h) 1fr calc(var(--nav-h) + var(--safe-b));
  animation:appIn var(--t-slow) var(--ease-out);
  overflow:hidden;
}
.app > *{ min-width:0; }
@keyframes appIn{
  from{ opacity:0; transform:translateX(-50%) scale(.98) }
  to{ opacity:1; transform:translateX(-50%) scale(1) }
}

/*
 * AISLAMIENTO DE LA CAPA DEL MENÚ.
 *
 * El menú no es DOM puro: dentro vive `isla-circuit-canvas` (los circuitos de
 * la isla). Un lienzo y el DOM que lo rodea son cosas distintas para el
 * compositor, y cuando el lienzo se ensucia el WebView de Android puede acabar
 * rehaciendo trabajo del subárbol entero.
 *
 * Estas cuatro declaraciones le dicen al navegador que esto es una capa propia
 * y acotada, así que lo que pase dentro no obliga a recalcular fuera:
 *
 *  · `isolation:isolate` — contexto de apilamiento propio; las mezclas de
 *    dentro no se resuelven contra lo de fuera.
 *  · `contain:layout style paint` — promete que nada de dentro cambia el
 *    maquetado ni el estilo de fuera, y que no se pinta fuera de la caja. Es
 *    seguro aquí porque `.screen-host` ya tenía `overflow:hidden` (o sea, ya
 *    recortaba) y las capas flotantes —popups, toasts, efectos— son HERMANAS
 *    de `#app`, no descendientes: no las puede recortar.
 *  · `translate3d(0,0,0)` — promoción a capa compuesta, la fórmula que el
 *    WebView respeta.
 *  · `will-change` — avisa antes de animar, para que no cree la capa a media
 *    transición, que es justo cuando se ve el salto.
 *
 * NO es el arreglo del parpadeo del tutorial: ése era el bucle perpetuo de
 * `ui/isla-touch.js`. Esto es la otra mitad del mismo problema —que ese lienzo
 * y el menú compartan destino de composición— y no cuesta nada.
 */
.screen-host{
  position:relative; overflow:hidden;
  isolation:isolate;
  contain:layout style paint;
  transform:translate3d(0, 0, 0);
  will-change:transform, opacity;
  /* Sin esto, en el WebView un gesto hacia abajo que empiece en el borde puede
     convertirse en «tirar para recargar» del sistema y robarse el toque con un
     `pointercancel` a medio gesto. El menú no scrollea el documento: no hay
     nada legítimo que ceder. */
  overscroll-behavior:contain;
}

/* ============ Layers ============ */
/*
 * LAS CAPAS DE ENCIMA VAN AISLADAS, IGUAL QUE `.screen`.
 *
 * ESTA ERA LA CAUSA DEL PARPADEO EN LA APK, y no las animaciones en si —
 * quitarles la mezcla y la mascara no cambio nada, que es lo que puso sobre la
 * pista.
 *
 * `.screen` ya llevaba `isolation`, `contain:layout style paint` y capa propia,
 * asi que lo que se pinta DENTRO de una pantalla se queda dentro. Estas tres no
 * llevaban nada: son `position:fixed` a secas. Sin contencion, el navegador no
 * puede saber que lo que cambia aqui no afecta a lo de abajo, asi que al abrir
 * un dialogo o lanzar una celebracion INVALIDA Y REPINTA la pagina entera — con
 * el mapa de la isla, sus doce edificios y su atmosfera dentro.
 *
 * En Chrome de escritorio eso se absorbe. En el WebView de Android es un
 * fotograma perdido y se ve como un parpadeo del cartel, de los botones y de
 * todo lo que hay detras.
 *
 * `contain:paint` recorta al propio rectangulo —son de viewport completo, asi
 * que no se pierde nada— y `translate3d` las promueve a capa de compositor para
 * que el repintado no baje al arbol principal.
 */
.popup-layer,.toast-layer,.fx-layer{
  position:fixed; top:0; bottom:0; pointer-events:none;
  isolation:isolate;
  contain:layout style paint;
  transform:translate3d(0, 0, 0);
}
/* El encuadre 9:16 las centra con `translateX(-50%)` (ver la regla de arriba):
   hay que conservarlo o se irian al borde. Se combina con la promocion. */
.popup-layer,.toast-layer,.fx-layer{ transform:translateX(-50%) translateZ(0); }
.popup-layer{ z-index:8000; }
.toast-layer{ z-index:8500; display:flex; flex-direction:column; align-items:center; padding-top:calc(var(--safe-t) + 68px); gap:8px; }
.fx-layer{ z-index:8600; overflow:hidden; }

/* ============ Mission ============ */
/* `touch-action:none` es OBLIGATORIO aquí, no un detalle.
   El `body` declara `manipulation`, que sigue cediendo al navegador los gestos
   de desplazamiento: en cuanto reconocía el arrastre como un "pan" se quedaba
   con él y disparaba `pointercancel` a media pasada. El comandante avanzaba un
   trozo y se paraba, y había que levantar el dedo y volver a deslizar. Con
   `none` el navegador no interpreta nada y el arrastre llega entero. */
.mission{
  position:fixed; top:0; bottom:0; z-index:5000; background:#03060E;
  touch-action:none;
  overscroll-behavior:contain;
}
.mission.hidden{ display:none; }
#world3d-canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; z-index:0; }
#game-canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; z-index:1; }

/* ============ Scrollbars ============ */
.scroll{ overflow-y:auto; -webkit-overflow-scrolling:touch; scrollbar-width:thin; scrollbar-color:var(--c-blue) transparent; }
.scroll::-webkit-scrollbar{ width:4px; }
.scroll::-webkit-scrollbar-thumb{ background:linear-gradient(var(--c-blue),var(--c-purple)); border-radius:99px; }

/* ============ Utilidades ============ */
.row{ display:flex; align-items:center; gap:8px; }
.col{ display:flex; flex-direction:column; }
.grow{ flex:1; }
.center{ display:flex; align-items:center; justify-content:center; }
.txt-dim{ color:var(--tx-mid); }
.txt-lo{ color:var(--tx-lo); }
.txt-neon{ color:var(--c-neon); }
.txt-gold{ color:var(--gold); }
.txt-bad{ color:var(--bad); }
.nowrap{ white-space:nowrap; }
.mono{ font-variant-numeric:tabular-nums; font-family:var(--f-num); }

/* =========================================================================
   PANTALLA DE ACCESO
   Por encima de la carga (z 9600) y por debajo de nada: es lo primero que ve
   un jugador nuevo. Todo el movimiento va por transform/opacity.
   ========================================================================= */
.login{
  position:fixed; top:0; bottom:0; z-index:9600;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:34px; padding:28px 22px calc(28px + var(--safe-b, 0px));
  background:radial-gradient(74% 52% at 50% 34%, #131c46 0%, #05070F 74%);
  transition:opacity var(--t-slow) var(--ease-out);
}
.login.out{ opacity:0; pointer-events:none; }

/* ---------------- Marca ---------------- */
.login-marca{ text-align:center; animation:loginEntra .55s var(--ease-back) backwards; }
@keyframes loginEntra{ from{ opacity:0; transform:translateY(16px) } }

.login-logo{
  width:112px; height:112px; margin:0 auto 18px;
  border-radius:28px;
  background:url("../icon-192.png") center / cover no-repeat, var(--bg-1);
  border:1px solid rgba(139,146,255,.26);
  box-shadow:var(--glow-blue), 0 18px 44px rgba(0,0,0,.6);
  animation:loginPulso 2.8s var(--ease-out) infinite;
}
@keyframes loginPulso{ 0%,100%{ transform:scale(1) } 50%{ transform:scale(1.045) } }

.login-titulo{
  margin:0; font-size:27px; font-weight:900; line-height:1.06; letter-spacing:.06em;
}
.login-titulo em{ font-style:normal; color:var(--c-neon); }

/* ---------------- Acciones ---------------- */
.login-acciones{
  width:100%; max-width:340px;
  display:flex; flex-direction:column; gap:11px;
  animation:loginEntra .55s var(--ease-back) .12s backwards;
}
.login-btn{ justify-content:center; }

/* El botón de Google va en blanco: sus normas de marca no admiten
   recolorear el logotipo ni ponerlo sobre fondos de bajo contraste. */
.login-google{
  display:flex; align-items:center; justify-content:center; gap:10px;
  background:#FFFFFF; color:#1F1F1F;
  border:1px solid rgba(0,0,0,.14);
  text-transform:none; letter-spacing:.01em; font-size:14px; font-weight:700;
}
.login-google::after{ display:none; }      /* sin el barrido de los botones del juego */
.login-google .g-logo{ flex:0 0 18px; }
.login-google.cargando{ opacity:.6; pointer-events:none; }

.login-nota{
  margin:6px 2px 0; text-align:center;
  font-size:11px; line-height:1.5; color:var(--tx-lo);
  transition:color var(--t-mid) var(--ease-out);
}
/* Fallo de acceso: en modo sólo-Google no hay a dónde ir, así que el motivo
   tiene que QUEDARSE en pantalla. Un toast se va antes de leerlo y el jugador
   se queda mirando el mismo botón sin saber qué pasó. */
.login-nota.login-error{
  color:var(--bad);
  font-weight:800;
}

/* ==================================================================== */
/* COLA DE CONSTRUCCIÓN / MEJORA EN BASE (SWIPE-TO-COLLAPSE)            */
/* ==================================================================== */
.base-build-queue {
  display: flex;
  flex-direction: column;
  gap: 6px;
  z-index: 20;
  width: 100%;
}

.base-build-edge-tabs {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  margin-left: -10px; /* Anclado al borde izquierdo de la pantalla */
  width: fit-content;
  z-index: 25;
}

/* Tarjeta expandida */
.base-build-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-md);
  background: rgba(10, 14, 28, 0.94);
  border: 1px solid rgba(100, 200, 255, 0.35);
  backdrop-filter: blur(10px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  touch-action: pan-y;
  user-select: none;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.22s ease-out;
  will-change: transform, opacity;
}

.base-build-row.arrastrando {
  transition: none !important;
}

.base-build-row.colapsando {
  transform: translateX(-120%) !important;
  opacity: 0 !important;
  pointer-events: none;
}

.base-build-swipe-hint {
  width: 22px;
  height: 28px;
  flex: 0 0 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.55;
  color: var(--tx-lo);
  border-radius: 4px;
  transition: opacity 0.15s, background 0.15s;
}
.base-build-swipe-hint:active,
.base-build-swipe-hint:hover {
  opacity: 1;
  background: rgba(100, 200, 255, 0.15);
  color: #fff;
}

/* Pestaña anclada al borde izquierdo (sobresale un pedazo para deslizar a la derecha) */
.base-build-edge-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 12px 0 8px;
  background: linear-gradient(90deg, rgba(8, 14, 28, 0.96) 0%, rgba(16, 26, 52, 0.94) 100%);
  border: 1px solid rgba(85, 230, 255, 0.5);
  border-left: none; /* Pegado al borde izquierdo del teléfono */
  border-radius: 0 18px 18px 0; /* Pestaña que sobresale hacia la derecha */
  box-shadow: 2px 4px 14px rgba(0, 0, 0, 0.6), 0 0 10px rgba(85, 230, 255, 0.2);
  cursor: pointer;
  user-select: none;
  touch-action: pan-y;
  animation: tabEntraBorde 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
  transition: transform 0.2s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.2s, border-color 0.2s;
  will-change: transform;
}

.base-build-edge-tab.arrastrando {
  transition: none !important;
}

.base-build-edge-tab:active {
  box-shadow: 2px 4px 18px rgba(85, 230, 255, 0.4), 0 0 14px rgba(85, 230, 255, 0.35);
  border-color: #55E6FF;
}

.base-build-edge-tab .edge-tab-icon {
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  border-radius: 50%;
  background: rgba(85, 230, 255, 0.16);
  border: 1px solid rgba(85, 230, 255, 0.35);
  display: grid;
  place-items: center;
  font-size: 13px;
}

.base-build-edge-tab .edge-tab-timer {
  font-size: 11px;
  font-weight: 800;
  font-family: monospace, var(--font);
  color: #FFD255;
  text-shadow: 0 0 6px rgba(255, 210, 85, 0.4);
  letter-spacing: 0.02em;
}

.base-build-edge-tab .edge-tab-arrow {
  font-size: 11px;
  color: #55E6FF;
  display: flex;
  align-items: center;
  opacity: 0.85;
  animation: arrowPulse 1.2s ease-in-out infinite alternate;
}

@keyframes tabEntraBorde {
  from {
    opacity: 0;
    transform: translateX(-40px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes arrowPulse {
  from { transform: translateX(0); opacity: 0.6; }
  to { transform: translateX(3px); opacity: 1; }
}

.ic--flip-x {
  transform: scaleX(-1);
}

