/* =========================================================================
   Pantallas del meta-juego
   ========================================================================= */
.screen{
  position:absolute; inset:0; display:flex; flex-direction:column;
  opacity:1; transform:translateZ(0);
  transition:opacity .06s ease-out;
  contain:layout paint;
  backface-visibility:hidden;
}
/* La pantalla saliente */
.screen.out{
  opacity:0 !important;
  pointer-events:none !important;
}

/*
 * PANTALLA DORMIDA EN CACHÉ.
 * `visibility:hidden` conserva el cálculo del maquetado y los recursos en GPU.
 */
.screen.off{
  visibility:hidden !important;
  opacity:0 !important;
  pointer-events:none !important;
}

.screen.precalentada{
  visibility:hidden !important;
  opacity:0 !important;
  pointer-events:none !important;
}

/* Una escena nueva se monta completa debajo de la actual. Sólo se descubre
   cuando `build()` terminó, evitando el fotograma vacío en WebView. */
.screen.preparando{
  visibility:hidden !important;
  opacity:0 !important;
  pointer-events:none !important;
}

/* Las escenas cacheadas conservan DOM y maquetado, pero no deben gastar GPU
   animando partículas, marcos o fondos mientras están ocultas. */
.screen.off *, .screen.preparando *{
  animation-play-state:paused !important;
}

/* Eliminar el pseudo-elemento de barrido que causaba destellos/parpadeos */
.screen::after{ display:none !important; }

.screen-body{ flex:1; overflow-y:auto; -webkit-overflow-scrolling:touch; padding:10px 10px 20px; }
.screen-body::-webkit-scrollbar{ width:0; }

/* ---------- BASE / HOME ---------- */
.base-view{
  position:relative; flex:1; overflow:hidden;
  background:
    radial-gradient(80% 60% at 50% 88%, rgba(46,107,255,.20), transparent 65%),
    linear-gradient(180deg,#070B18 0%,#0C1226 55%,#101A38 100%);
}
.base3d-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;z-index:0;pointer-events:none;opacity:0;transition:opacity .55s ease}
.base3d-on .base3d-canvas{opacity:1}
.base3d-canvas.base3d-slide,.base-view.base3d-dragging .base3d-canvas{filter:saturate(1.15) brightness(1.06);transform:scale(1.015);transition:filter .34s ease,transform .34s cubic-bezier(.22,1,.36,1)}
.base3d-locked .base3d-canvas{filter:blur(4px) brightness(.48) saturate(.55)}
.base3d-on .isla{background:radial-gradient(ellipse at 50% 44%,rgba(12,44,78,.96),rgba(3,13,31,.98) 72%) !important}
.base3d-on .isla-pager{z-index:1;background:transparent}
.base3d-on .isla-suelo .mapa-svg{opacity:1}
.base3d-on .isla-suelo .isla-vineta{opacity:1}
.base3d-on .isla-atmos,.base3d-on .isla-circuit-canvas,.base3d-on .worker-track{display:block}
.base3d-on .sky-traffic{display:block !important}
.base3d-on .isla-suelo .base-slots .bimg,.base3d-on .isla-suelo .base-slots .bname{opacity:1}
.base3d-on .isla-suelo .base-slots .bslot{z-index:20}
/* En WebGL las cinco islas viven en un único archipiélago. Los nodos DOM se
   conservan sólo como hit targets del sector activo, nunca como cinco fondos
   superpuestos ni como una tira de escenas independientes. */
.base3d-on .isla-tira{position:absolute;inset:0;width:100%;display:block;transform:none !important;transition:none !important}
.base3d-on .isla-tira>.isla{position:absolute;inset:0;width:100%;height:100%;opacity:0;pointer-events:none}
.base3d-on .isla-tira>.isla.activa{opacity:1;pointer-events:auto}
.base3d-on .isla-tira>.isla:not(.activa) .isla-rotulo{opacity:0}
.base3d-on .isla-tira>.isla.activa .isla-rotulo{opacity:1}
.entra .base-view{
  /* Animación instantánea sin barridos para evitar parpadeos */
}
.entra .base-view::before{
  display: none !important;
}
/* ---------- ISLAS (vista cenital) ---------- */
/* El paginador ocupa la vista entera y las islas van en una tira horizontal;
   deslizar sólo mueve `transform`, que es lo único que el compositor puede
   animar sin repintar. */
/*
 * `touch-action:none` Y NO `pan-y`. Éste es el motivo de que deslizar entre
 * islas funcionara con ratón y NO con el dedo en el teléfono.
 *
 * Con `pan-y` el navegador se reserva el derecho de decidir que un toque es un
 * desplazamiento vertical. Esa decisión la toma ÉL, con sus primeros ~10 px de
 * movimiento, ANTES de que nuestro JavaScript pueda opinar — y un pulgar
 * arranca prácticamente siempre con algo de componente vertical, porque la
 * mano gira sobre la muñeca. En cuanto el navegador reclamaba el gesto,
 * mandaba `pointercancel`: la tira volvía a su sitio y el arrastre moría a
 * medias. Se vivía como «desliza hasta un tope y no pasa a la siguiente base».
 * Los eventos sintéticos de las pruebas no pasan por ese árbitro de gestos,
 * y por eso las pruebas pasaban mientras el teléfono fallaba.
 *
 * `none` es correcto AQUÍ porque el paginador ocupa la vista entera de la
 * Base y en esa zona no hay nada que desplazar verticalmente: el reparto
 * vertical/horizontal ya lo hace `montarDeslizamiento` (con margen 1,4 para
 * el temblor del pulgar), y el pellizco de la cámara también es nuestro
 * (`isla-touch.js`) — con `none` le llegan TODOS los eventos, también mejor.
 */
.isla-pager{ position:absolute; inset:0; overflow:hidden; touch-action:none; user-select:none; -webkit-user-select:none; }
.isla-tira{
  display:flex; height:100%; width:100%;
  transition:transform .34s cubic-bezier(.22, 1, .36, 1);
  will-change:transform;
  touch-action:none;
}
/* Durante el arrastre se quita la transición: si no, el dedo va por delante
   del contenido y el gesto se siente pegajoso. */
.isla-pager.arrastrando .isla-tira{ transition:none !important; }
/*
 * SIN `perspective`. La llevaba para las rotaciones 3D que se han retirado
 * (ver `ui/isla-touch.js`): la base es una pintura en proyección dimétrica, no
 * una escena 3D, y rotarla la ladeaba como una foto en vez de mover la cámara.
 *
 * Dejarla puesta no era inocuo: `perspective` convierte al elemento en bloque
 * contenedor de sus descendientes posicionados, y eso cambia a qué se anclan
 * las capas de la isla.
 */
.isla{
  position:relative; flex:0 0 100%; height:100%;
  touch-action:none; user-select:none; -webkit-user-select:none;
  backface-visibility:hidden;
  contain:layout style;
  transform:translateZ(0);
}

/*
 * CAJA CUADRADA DEL SUELO. Es lo que hace que la base se vea en 3D.
 *
 * El SVG del mapa tiene `viewBox="0 0 100 100"` y `preserveAspectRatio="none"`,
 * o sea que se ESTIRA hasta llenar a su contenedor. Mientras ese contenedor fue
 * la isla entera —unos 375 x 600 en un móvil— una unidad de mapa medía 3,75 px
 * de ancho y 6 px de alto, y todo lo dibujado salía aplastado en horizontal al
 * 62 %. La costa está calculada para verse 1,92:1, la proporción dimétrica de
 * los edificios, y llegaba a la pantalla como 1,21:1: casi un círculo. De ahí
 * que la base se leyera como una foto cenital pese a tener los volúmenes bien.
 *
 * Con la caja cuadrada la unidad de mapa mide lo mismo en los dos ejes, así que
 * la proporción calculada es la que se ve. Y las capas van todas DENTRO de
 * ella, no sueltas sobre la isla, porque comparten sistema de coordenadas.
 *
 * Centrado con `translate` y no con `transform`: el `transform` de esta caja lo
 * escribe la cámara de `isla-touch.js` en cada fotograma y borraría el centrado.
 */
.isla-suelo{
  position:absolute; left:50%; width:100%; aspect-ratio:1 / 1;
  top:50%; translate:-50% -50%;
  transform-origin:50% 55%;
  transform:translateZ(0);
  backface-visibility:hidden;
  contain:layout paint;
  isolation:isolate;
}
/*
 * En vertical —que es como está bloqueado el juego— el ancho es el lado corto y
 * la caja cabe siempre. En una ventana más ancha que alta no: el cuadrado se
 * saldría por arriba y por abajo y `.isla-pager` recortaría la costa. Ahí manda
 * la altura y el ancho sale de la proporción.
 *
 * ESTO ERA `width: min(100%, 100cqh)` CON UN CONTENEDOR DE CONSULTA, y estaba
 * mal: si `.isla-suelo` se pinta sin ese contenedor delante —o el navegador
 * informa de una altura de cero, cosa que pasa en un panel que todavía no
 * compone— `cqh` vale 0, el `min()` vale 0 y LA BASE ENTERA DESAPARECE. Medido:
 * la caja daba 0 x 0 px. Una consulta de medios no tiene ese modo de fallo,
 * porque no depende de que exista ningún ancestro concreto.
 */
@media (min-aspect-ratio: 1/1){
  .isla-suelo{ width:auto; height:100%; }
}
.mapa-svg{
  position:absolute; inset:0; width:100%; height:100%;
  transform:translateZ(0);
  backface-visibility:hidden;
  contain:paint;
}
.base-slots{ position:absolute; inset:0; }
.isla-circuit-canvas{ position:absolute; inset:0; width:100%; height:100%; pointer-events:none; z-index:2; }
/* Halo del agua alrededor de la costa: separa la isla del fondo sin un borde
   duro, que a esta escala se vería como un recorte de papel. */
.mp-halo{ fill:none; stroke-width:3.4; stroke-opacity:.20; filter:blur(2.2px); }
/* Caminos finos y con línea de eje. Gruesos se leían como vigas grises tiradas
   sobre el mapa; lo que tienen que hacer es guiar la mirada hacia el centro sin
   competir con los edificios. */
.mp-camino{ stroke:rgba(150,190,255,.14); stroke-width:1.5; stroke-linecap:round; fill:none; }
.mp-camino-eje{ stroke:rgba(190,220,255,.20); stroke-width:.22; stroke-dasharray:1.6 1.8; fill:none; }
.mp-parcela{ fill:rgba(150,190,255,.055); stroke:rgba(150,190,255,.16); stroke-width:.3; }

.isla-rotulo{
  position:absolute; top:8px; left:0; right:0; text-align:center; pointer-events:none; z-index:10;
}
.isla-rotulo b{ display:block; font-size:13px; letter-spacing:2px; text-transform:uppercase; text-shadow:0 2px 6px rgba(0,0,0,.85); }
.isla-rotulo span{ display:block; font-size:10px; color:var(--tx-lo); letter-spacing:.8px; text-shadow:0 1px 4px rgba(0,0,0,.8); }

/* Isla aún no desplegada: se ve el terreno por debajo, atenuado sin filtros GPU pesados. */
.isla.bloqueada .isla-suelo{ opacity:.30; }
.isla.bloqueada .mapa-svg{ filter:none; }
.isla.bloqueada .isla-atmos{ display:none !important; }
.isla-cerrojo{
  position:absolute; inset:0; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:6px; text-align:center; padding:0 26px;
  background:radial-gradient(60% 50% at 50% 50%, rgba(5,7,15,.72), rgba(5,7,15,.9));
}
.base3d-on .isla.bloqueada .isla-cerrojo{z-index:400;overflow:hidden;background:radial-gradient(45% 42% at 50% 50%,rgba(8,18,45,.62),rgba(2,5,17,.93) 74%);backdrop-filter:blur(2px)}
.base3d-on .isla.bloqueada .isla-cerrojo::before,.base3d-on .isla.bloqueada .isla-cerrojo::after{content:"";position:absolute;left:-28%;width:156%;height:24px;border:3px solid rgba(255,190,72,.82);background:repeating-linear-gradient(90deg,rgba(255,219,126,.82) 0 12px,rgba(100,52,10,.9) 12px 19px,rgba(255,184,53,.8) 19px 31px);box-shadow:0 0 14px rgba(255,164,35,.56),inset 0 0 6px rgba(255,232,156,.65);opacity:.78;pointer-events:none;z-index:0}
.base3d-on .isla.bloqueada .isla-cerrojo::before,.base3d-on .isla.bloqueada .isla-cerrojo::after{display:none}
.base3d-on .isla.bloqueada .isla-cerrojo::before{top:44%;transform:rotate(22deg);animation:cadenaDiagonalA 2.8s ease-in-out infinite}
.base3d-on .isla.bloqueada .isla-cerrojo::after{top:52%;transform:rotate(-22deg);animation:cadenaDiagonalB 3.2s ease-in-out infinite}
@keyframes cadenaDiagonalA{50%{filter:brightness(1.45);transform:rotate(22deg) translateX(10px)}}
@keyframes cadenaDiagonalB{50%{filter:brightness(1.45);transform:rotate(-22deg) translateX(-10px)}}
.base3d-on .isla.bloqueada .isla-cerrojo>*{position:relative;z-index:2}
.isla-cerrojo-ico{ font-size:30px; color:#c9e7ff;filter:drop-shadow(0 0 16px #3faaff);transform:scale(1.65);margin-bottom:12px; }
.isla-cerrojo b{ font-size:20px; letter-spacing:1.5px;text-transform:uppercase;text-shadow:0 0 12px rgba(126,198,255,.7); }
.isla-cerrojo p{ margin:0; font-size:12.5px; color:var(--tx-mid); max-width:30ch; }
.isla-faltan{
  margin-top:4px; padding:4px 12px; border-radius:999px; font-size:11px; font-weight:700;
  color:var(--warn); background:rgba(255,197,61,.10); border:1px solid rgba(255,197,61,.32);
}

/* Navegación entre islas */
.isla-nav{ position:absolute; left:0; right:0; top:40px; pointer-events:none; z-index:10; }
.isla-dots{ display:flex; justify-content:center; gap:7px; pointer-events:auto; }
.isla-dot{
  width:7px; height:7px; padding:0; border-radius:50%; border:0;
  background:rgba(255,255,255,.22); transition:all var(--t-fast);
}
.isla-dot.on{ width:20px; border-radius:99px; background:var(--c-neon); box-shadow:var(--glow-neon); }
.isla-dot.lock{ background:rgba(255,255,255,.10); }
.isla-flecha{
  position:absolute; top:26px; width:34px; height:44px; padding:0; border:0;
  display:grid; place-items:center; pointer-events:auto;
  background:linear-gradient(90deg, rgba(10,16,34,0), rgba(10,16,34,.75));
  color:var(--tx-mid); font-size:20px;
  animation:islaLatido 1.8s var(--ease-out) infinite;
}
.isla-flecha.der{ right:0; border-radius:14px 0 0 14px; }
.isla-flecha.izq{ left:0; border-radius:0 14px 14px 0; transform:scaleX(-1); }
@keyframes islaLatido{ 0%,100%{ opacity:.55 } 50%{ opacity:1 } }

.base-slots{ position:absolute; inset:0; }
/*
 * EL EDIFICIO SE APOYA EN LA PARCELA, NO SE CENTRA EN ELLA.
 *
 * Con dibujos planos el centro del sprite iba sobre el centro de la parcela.
 * En volumen eso es falso: un edificio ocupa su planta a ras de suelo y crece
 * HACIA ARRIBA, así que lo que tiene que coincidir con la parcela es su BASE.
 * Centrándolo, un edificio alto quedaba medio hundido en el terreno y uno bajo
 * flotando.
 *
 * El ancla del lienzo está en (32,46) de un viewBox de 64, o sea al 71,9 % de
 * la altura: de ahí el segundo valor del `translate`. Si se cambia `ANCLA_Y` en
 * `ui/basemap.js`, hay que cambiarlo aquí.
 *
 * El rótulo va absoluto por debajo, fuera del flujo, para que no pueda
 * desplazar el cuerpo ni con un nombre de dos líneas.
 */
/*
 * `--prof` gradúa el tamaño según la profundidad: 0.92 en la fila del fondo,
 * 1.04 en la de delante (lo escribe `screens/base.js`). Sin ese gradiente la
 * escena es una axonometría pura y el ojo la lee como un plano técnico.
 *
 * El valor por defecto está aquí y no sólo en JS para que un `.bslot` pintado
 * fuera de la isla —el tutorial, una previsualización— no salga a tamaño cero.
 */
.bslot{
  --prof:1;
  position:absolute; transform:translate(-50%,-71.9%) scale(var(--prof));
  /*
   * EL ORIGEN DE LA ESCALA ES EL PUNTO QUE PISA EL SUELO, no el centro de la
   * caja. El edificio está anclado al 71,9 % de su lienzo —ahí es donde apoya—,
   * y escalar alrededor del centro DESPLAZA ese punto: medido, el gradiente de
   * profundidad despegaba los edificios 1,4 px de su losa, y lo mismo hacía el
   * hundido al pulsar.
   */
  transform-origin:50% 71.9%;
  width:92px; height:92px; display:grid; place-items:center;
  /*
   * `--ease-out` y NO `--ease-back`. La curva `back` sobrepasa el destino y
   * vuelve, y al SOLTAR el dedo eso se ve como el botón estirándose un instante
   * — que es exactamente lo que se reportó («lo estira al botón por un
   * segundo»). Se corrigió en el resto de pulsables y este se quedó atrás.
   */
  transition:transform var(--t-fast) var(--ease-out), filter var(--t-fast) var(--ease-out);
  background:none; border:0; padding:0;
}
/*
 * Al pulsar, el edificio SE HUNDE en su losa en vez de encogerse en el sitio:
 * baja un poco y pierde escala, como si lo apretaras contra el suelo. Es el
 * mismo gesto que hace un botón físico, y aquí además refuerza que hay un
 * plano de suelo debajo.
 */
.bslot:active{ transform:translate(-50%,-70.6%) scale(calc(var(--prof) * .94)); }
.bslot:active .bimg{ filter:brightness(1.18); }
.bslot .bimg{
  width:92px; height:92px; border-radius:0; position:relative;
  display:grid; place-items:center;
  background:none; border:0; box-shadow:none;
}
/* La planta construida no lleva marco: el marco es lo que la haría parecer un
   botón puesto sobre el mapa en vez de un edificio plantado en el suelo. */
/* ---------- EDIFICIOS EN VOLUMEN ----------
   Sombreado plano de tres tonos: techo claro, cara izquierda media y cara
   derecha oscura. Es lo que hace que un prisma se lea como un cuerpo sin
   necesidad de luces ni degradados — y con un solo juego de tonos para los
   doce, el escenario parece construido por la misma gente. */
.v-edificio{ width:100%; height:100%; overflow:visible; }
.v-techo{ fill:#48619F; }
.v-izq{ fill:#2C3D70; }
.v-der{ fill:#212F58; }
.v-cupula{ fill:#3E5590; }
.v-acento.v-techo{ fill:#5AC8F5; }
.v-acento.v-izq{ fill:#2F86B8; }
.v-acento.v-der{ fill:#246A96; }
.v-acento.v-cupula{ fill:#3FA6DC; }
/* Contorno finísimo común: separa una caja de la de al lado cuando se tocan. */
.v-techo, .v-izq, .v-der, .v-cupula{ stroke:rgba(10,16,34,.55); stroke-width:.35; }
.v-suelo{ fill:rgba(150,190,255,.10); stroke:rgba(150,190,255,.22); stroke-width:.4; }
.v-helipuerto{ fill:rgba(90,200,245,.16); stroke:rgba(90,200,245,.5); stroke-width:.5; }
.v-patio{ fill:rgba(10,16,34,.5); stroke:rgba(150,190,255,.22); stroke-width:.4; }
.v-cimiento{ fill:rgba(150,190,255,.07); stroke:rgba(150,190,255,.34);
             stroke-width:.7; stroke-dasharray:2.4 2; }
.v-mastil{ fill:none; stroke:#8FB6E8; stroke-width:.7; stroke-linecap:round; }
.v-luz{ fill:#7CE0FF; }
/* Mancha en el suelo, no una copia del cuerpo: un volumen proyecta una sombra
   plana, y una silueta idéntica corrida se leería como un segundo edificio.

   Ahora la mancha es una ELIPSE ALARGADA en la dirección de la luz (ver
   `edificioSvg`), no un disco. El desenfoque sube en consecuencia: una sombra
   dura delata que es un polígono, y a esta distancia del foco el borde tiene
   que estar claramente difuminado. Baja la opacidad al mismo tiempo porque una
   mancha más grande con la misma densidad ensucia el suelo. */
.v-sombra{ fill:rgba(2,4,10,.42); filter:blur(2.2px); }

/* La parcela libre comparte lienzo con el edificio: si midieran distinto, al
   terminar la obra el edificio daría un salto de sitio. */
.bslot .bimg.vacia{ position:relative; }
/* El símbolo del edificio pendiente flota sobre su losa, centrado en el ancla
   (71,9 % del lienzo) y no en la caja: si no, queda descolgado hacia abajo. */
.bslot .bicono{
  position:absolute; left:50%; top:71.9%; transform:translate(-50%,-115%);
  font-size:23px; color:var(--tx-lo); opacity:.62; pointer-events:none;
}
.bslot .blvl{
  position:absolute; bottom:-6px; right:-6px; min-width:20px; padding:1px 5px; border-radius:6px;
  background:var(--bg-0); border:1px solid var(--c-neon); color:var(--c-neon);
  font-size:10px; font-weight:800; text-align:center;
}
/* Fuera del flujo y colgando por debajo: es lo que impide que el rótulo vuelva
   a desplazar el edificio respecto a su parcela. */
.bslot .bname{
  position:absolute; top:100%; left:50%; transform:translateX(-50%);
  width:96px; margin-top:-20px; pointer-events:none;
  font-size:9.5px; line-height:1.15; letter-spacing:.4px;
  color:var(--tx-mid); text-align:center; text-transform:uppercase;
  text-shadow:0 1px 3px rgba(0,0,0,.9);
}
/*
 * LA FLECHA DE MEJORA VA ABAJO, AL PIE DEL EDIFICIO.
 *
 * Estaba en la esquina SUPERIOR IZQUIERDA del lienzo, que es el peor sitio
 * posible por dos motivos: se sale por arriba del volumen —flotando sobre el
 * cielo, sin tocar nada— y en la isla principal cae justo donde están los
 * atajos de Ruleta, Lab ADN y Bestias, así que se pisaban entre ellos.
 *
 * Al pie y ligeramente a la derecha queda apoyada en el terreno, junto al
 * edificio al que se refiere, y libre tanto de la esquina de los atajos como
 * del rótulo del nombre, que va centrado más abajo.
 *
 * El 71,9 % es el ancla del sprite: el punto exacto donde el edificio toca el
 * suelo. Referir la flecha a esa altura, y no a la caja, la mantiene pegada a
 * la base aunque el edificio crezca de nivel.
 */
.bslot .up-ready{
  position:absolute; top:71.9%; left:66%; width:20px; height:20px; border-radius:50%;
  /* Centrado con márgenes y no con `transform`: el `transform` es de `bob`, y
     ponerlo aquí anularía el rebote. */
  margin:-10px 0 0 -10px;
  background:var(--g-action); color:#032312; font-size:13px; font-weight:900; display:grid; place-items:center;
  animation:bob 1.1s var(--ease-out) infinite; box-shadow:var(--glow-neon);
  /* Por encima del edificio de al lado: las columnas están a unos 100 px y la
     caja mide 92, así que sin esto la flecha del de la izquierda podía quedar
     medio tapada por el vecino. */
  z-index:3;
}
@keyframes bob{ 0%,100%{transform:translateY(0)} 50%{transform:translateY(-5px)} }
.bslot .bwork{
  position:absolute; top:-8px; left:50%; transform:translateX(-50%);
  font-size:9.5px; padding:1px 6px; border-radius:99px; background:rgba(5,7,15,.9);
  border:1px solid rgba(255,197,61,.5); color:var(--warn); white-space:nowrap;
}

/* Panel flotante de acción principal */
.hero-cta{
  position:absolute; left:10px; right:10px; bottom:10px; z-index:500; pointer-events:auto;
  display:flex; flex-direction:column; gap:8px;
}
/* Con banner activo se sube todo para que nada tape la acción principal. */
.has-banner .hero-cta{ bottom:60px; }
.has-banner .screen-body{ padding-bottom:66px; }
.base-view > .hero-cta{position:absolute !important;top:auto !important;left:10px !important;right:10px !important;bottom:10px !important;z-index:500 !important}
.base-view.has-banner > .hero-cta{bottom:60px !important}
.next-goal{
  display:flex; align-items:center; gap:10px; padding:9px 12px; border-radius:var(--r-md);
  background:rgba(10,14,28,.88); border:1px solid var(--stroke); backdrop-filter:blur(10px);
}
.next-goal .gi{ font-size:20px; }
.next-goal .gt{ flex:1; min-width:0; }
.next-goal .gt b{ display:block; font-size:12.5px; }
.next-goal .gt span{ display:block; font-size:10.5px; color:var(--tx-lo); }

/* ---------- LISTAS GENÉRICAS ---------- */
.list{ display:flex; flex-direction:column; gap:8px; }
.list-row{
  display:flex; align-items:center; gap:10px; padding:10px;
  border-radius:var(--r-md); background:var(--panel-soft); border:1px solid var(--stroke);
}
.list-row .ri{
  width:46px; height:46px; flex:0 0 46px; border-radius:12px; display:grid; place-items:center; font-size:22px;
  background:linear-gradient(160deg,#22305E,#111938);
  border:1px solid var(--stroke);
}
.list-row .rt{ flex:1; min-width:0; }
.list-row .rt b{ display:block; font-size:13.5px; }
.list-row .rt span{ display:block; font-size:11px; color:var(--tx-lo); }
.list-row .ra{ flex:0 0 auto; text-align:right; }

/* ---------- GRIDS ---------- */
.grid2{ display:grid; grid-template-columns:repeat(2,1fr); gap:8px; }
.grid3{ display:grid; grid-template-columns:repeat(3,1fr); gap:8px; }
.grid4{ display:grid; grid-template-columns:repeat(4,1fr); gap:6px; }
.grid-auto{ display:grid; grid-template-columns:repeat(auto-fill,minmax(104px,1fr)); gap:8px; }

/* ---------- HÉROES ---------- */
.hero-card{ position:relative; padding:8px; text-align:center; }
.utc-art-img{position:absolute;inset:4%;width:92%;height:92%;object-fit:contain;pointer-events:none;user-select:none;-webkit-user-drag:none;filter:drop-shadow(0 8px 9px rgba(0,0,0,.58))}
.hero-portrait{
  position:relative; width:100%; aspect-ratio:1/1; border-radius:12px; overflow:hidden;
  display:grid; place-items:center; font-size:34px;
  background:linear-gradient(165deg, color-mix(in srgb,var(--rc) 34%, #101733), #0A0F22);
  border:1px solid color-mix(in srgb,var(--rc) 50%, transparent);
}
.hero-card-meta{ display:grid; grid-template-rows:15px 15px 12px; gap:1px; margin-top:4px; min-width:0; }
.hero-card .hname{ min-width:0; font-size:10px; line-height:15px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.hero-card .hstars{ font-size:9px; color:var(--gold); letter-spacing:-1px; }
.hero-card .hlvl{
  position:absolute; top:12px; left:12px; padding:1px 5px; border-radius:6px;
  background:rgba(5,7,15,.85); border:1px solid var(--stroke); font-size:10px;
}
.hero-card .shard-bar{ margin-top:4px; }
.rar-tag{
  position:static; display:block; width:100%; min-width:0; height:15px; padding:1px 4px;
  overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
  font-size:8px; line-height:13px; font-weight:900; letter-spacing:.55px; text-transform:uppercase;
  background:linear-gradient(90deg,transparent,var(--rar-color) 16%,var(--rar-color) 84%,transparent);
  color:#07101d; text-shadow:0 1px rgba(255,255,255,.2);
}

/* ---------- TECH TREE ---------- */
.tech-branch{ margin-bottom:14px; }
.tech-node{
  display:flex; align-items:center; gap:10px; padding:9px 10px; border-radius:var(--r-md);
  background:var(--panel-soft); border:1px solid var(--stroke); position:relative;
}
.tech-node.maxed{ border-color:rgba(255,211,78,.45); }
.tech-node .tn-ico{ width:40px; height:40px; flex:0 0 40px; border-radius:10px; display:grid; place-items:center; font-size:19px; background:var(--g-panel); border:1px solid var(--stroke); }
.tech-node .tn-lv{ font-size:11px; color:var(--tx-lo); }

/* ---------- TIENDA ---------- */
.shop-hero{
  position:relative; border-radius:var(--r-lg); overflow:hidden; padding:14px;
  background:linear-gradient(135deg, rgba(255,138,0,.28), rgba(255,61,127,.22)), #14102A;
  border:1px solid rgba(255,211,78,.45);
  box-shadow:0 0 30px rgba(255,138,0,.22);
}
.offer{
  position:relative; display:flex; flex-direction:column; gap:6px; padding:10px;
  border-radius:var(--r-md); background:var(--panel-soft); border:1px solid var(--stroke); overflow:hidden;
}
.offer .tagline{
  position:absolute; top:8px; right:-30px; transform:rotate(38deg);
  background:var(--g-danger); color:#fff; font-size:9.5px; font-weight:900;
  padding:2px 32px; letter-spacing:.6px; box-shadow:0 2px 8px rgba(0,0,0,.5);
}
.offer .price{
  margin-top:auto; padding:8px; border-radius:10px; text-align:center;
  background:var(--g-action); color:#032312; font-weight:900; font-size:13.5px;
}
.offer .price.real{ background:var(--g-premium); color:#2A1300; }
.offer .price.ad{ background:var(--g-primary); color:#fff; }
.offer .old{ text-decoration:line-through; color:var(--tx-lo); font-size:11px; }
.offer .val-tag{
  align-self:flex-start; padding:1px 6px; border-radius:5px; font-size:9.5px; font-weight:800;
  background:rgba(57,255,136,.16); color:var(--c-neon); border:1px solid var(--stroke-hot);
}
.vip-card{ background:linear-gradient(135deg,#2B1B57,#0E1030); border-color:rgba(196,166,255,.45); }

/* Card destacada del Titán Mecha Goliat en la Tienda */
.mecha-titan-hero-pack {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  padding: 14px;
  background: radial-gradient(circle at 85% 15%, rgba(255, 180, 0, 0.24), transparent 50%),
              linear-gradient(135deg, rgba(6, 15, 35, 0.95), rgba(18, 12, 38, 0.96));
  border: 1.5px solid rgba(255, 215, 0, 0.5);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.7), 0 0 25px rgba(255, 180, 0, 0.25);
  margin-bottom: 14px;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

@keyframes packHighlightPulse {
  0% {
    box-shadow: 0 0 0 rgba(255, 215, 0, 0);
    border-color: rgba(255, 215, 0, 0.5);
    transform: scale(1);
  }
  25% {
    box-shadow: 0 0 40px rgba(255, 215, 0, 0.95), 0 0 20px rgba(0, 229, 255, 0.75);
    border-color: #ffd700;
    transform: scale(1.025);
  }
  50% {
    box-shadow: 0 0 16px rgba(255, 215, 0, 0.4);
    border-color: rgba(255, 215, 0, 0.7);
    transform: scale(1);
  }
  75% {
    box-shadow: 0 0 40px rgba(255, 215, 0, 0.95), 0 0 20px rgba(0, 229, 255, 0.75);
    border-color: #ffd700;
    transform: scale(1.025);
  }
  100% {
    box-shadow: 0 0 22px rgba(255, 215, 0, 0.35);
    border-color: rgba(255, 215, 0, 0.6);
    transform: scale(1);
  }
}

.pack-card-highlighted {
  animation: packHighlightPulse 2.8s cubic-bezier(0.25, 1, 0.5, 1) !important;
  border-color: #ffd700 !important;
}



.bp-track{ display:flex; gap:8px; overflow-x:auto; padding:10px 4px; scroll-snap-type:x proximity; }
.bp-track::-webkit-scrollbar{ height:0; }
.bp-tier{
  flex:0 0 78px; scroll-snap-align:center; display:flex; flex-direction:column; gap:6px; align-items:center;
  /*
   * Los niveles fuera de la ventana del scroll NI SE MAQUETAN NI SE PINTAN:
   * con ~50 niveles y 4 a la vista, el navegador se ahorra >90 % del trabajo
   * de esta pista en cada pintado. `auto 78px auto 190px` es la estimacion
   * (ancho fijo real, alto tipico) con la que se reserva sitio hasta que el
   * nivel se ve por primera vez; despues el navegador recuerda el real.
   */
  content-visibility:auto;
  contain-intrinsic-size:auto 78px auto 190px;
}
.bp-tier .lvl{ font-size:11px; font-weight:800; padding:2px 8px; border-radius:99px; background:var(--glass); border:1px solid var(--stroke); }
.bp-tier.done .lvl{ background:var(--g-action); color:#032312; border-color:transparent; }
.bp-cell{
  width:100%; aspect-ratio:1/1; border-radius:12px; display:grid; place-items:center; position:relative;
  background:var(--panel-soft); border:1px solid var(--stroke); font-size:20px;
}
.bp-cell.prem{ background:linear-gradient(160deg,rgba(255,211,78,.20),rgba(255,61,127,.12)); border-color:rgba(255,211,78,.45); }
/* El check y el candado se dibujan con SVG en línea usado como máscara: la
   propiedad `content` sólo admite texto, así que antes eran un carácter y un
   icono —dibujados por el sistema y distintos en cada aparato—. Con
   `mask-image` el color lo pone `background`, así que siguen el tema. */
.bp-cell.claimed::after, .bp-cell.locked::after{
  content:""; position:absolute; inset:0;
  background-color:var(--c-neon);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-position:center; mask-position:center;
  -webkit-mask-size:22px 22px; mask-size:22px 22px;
}
.bp-cell.claimed::before, .bp-cell.locked::before{
  content:""; position:absolute; inset:0; background:rgba(5,7,15,.78);
}
.bp-cell.claimed::after{
  -webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4.8 12.6l4.8 4.8L19.2 7.8" fill="none" stroke="black" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4.8 12.6l4.8 4.8L19.2 7.8" fill="none" stroke="black" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg>');
}
.bp-cell.locked::before{ background:rgba(5,7,15,.72); }
.bp-cell.locked::after{
  background-color:#8A97B8;
  -webkit-mask-size:18px 18px; mask-size:18px 18px;
  -webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7.4 10V7.6a4.6 4.6 0 0 1 9.2 0V10" fill="none" stroke="black" stroke-width="2"/><rect x="4.8" y="10" width="14.4" height="10.4" rx="2.4" fill="black"/></svg>');
  mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7.4 10V7.6a4.6 4.6 0 0 1 9.2 0V10" fill="none" stroke="black" stroke-width="2"/><rect x="4.8" y="10" width="14.4" height="10.4" rx="2.4" fill="black"/></svg>');
}
.bp-cell .amt{ position:absolute; bottom:2px; right:4px; font-size:9.5px; font-weight:800; text-shadow:0 1px 3px #000; }

/* ---------- EVENTOS ---------- */
.event-card{
  position:relative; border-radius:var(--r-lg); padding:12px; overflow:hidden;
  border:1px solid var(--stroke); min-height:104px;
  display:flex; flex-direction:column; justify-content:space-between;
}
.event-card .ebg{ position:absolute; inset:0; opacity:.9; }
.event-card > *{ position:relative; }
.event-card h4{ margin:0; font-size:15px; letter-spacing:1px; text-transform:uppercase; }
.event-card p{ margin:2px 0 0; font-size:11px; color:var(--tx-mid); }

/* ---------- CAMPAÑA ---------- */
.stage-row{ display:flex; align-items:center; gap:10px; padding:10px; border-radius:var(--r-md); background:var(--panel-soft); border:1px solid var(--stroke); }
.stage-row.cur{ border-color:var(--c-neon); box-shadow:var(--glow-neon); }
.stage-row .sn{ width:42px; height:42px; flex:0 0 42px; border-radius:10px; display:grid; place-items:center; font-size:15px; font-weight:900; background:var(--g-panel); border:1px solid var(--stroke); }
.stars{ color:var(--gold); letter-spacing:-1px; font-size:12px; }
.stars .off{ color:rgba(255,255,255,.16); }

/* ---------- ARENA / LIGA ---------- */
.league-badge{
  width:56px; height:56px; border-radius:14px; display:grid; place-items:center; font-size:26px;
  background:linear-gradient(160deg,var(--rc,#8A97B8),#0A0F22); border:1px solid rgba(255,255,255,.2);
}
.rank-row{ display:flex; align-items:center; gap:9px; padding:8px 10px; border-radius:10px; background:var(--panel-soft); border:1px solid var(--stroke); font-size:12.5px; }
.rank-row .pos{ width:26px; font-weight:900; text-align:center; color:var(--tx-mid); }
.rank-row.me{ border-color:var(--c-neon); background:rgba(57,255,136,.10); }

/* ---------- CLAN / CHAT ---------- */
.chat{ display:flex; flex-direction:column; gap:6px; }
.msg{ display:flex; gap:7px; font-size:12px; }
.msg .who{ color:var(--c-blue-glow); font-weight:800; flex:0 0 auto; }
.msg .txt{ color:var(--tx-mid); }
.msg.sys .txt{ color:var(--c-neon); font-style:italic; }
.chat-input{ display:flex; gap:6px; padding:8px 0; }
.chat-input input{
  flex:1; padding:9px 12px; border-radius:99px; font-family:inherit; font-size:13px;
  background:var(--glass); border:1px solid var(--stroke); color:var(--tx-hi); outline:none;
}

/* ---------- SKINS ---------- */
.skin-card{ text-align:center; padding:8px; }
.skin-prev{
  width:100%; aspect-ratio:1/1; border-radius:12px; display:grid; place-items:center; font-size:30px;
  border:1px solid var(--stroke);
}

/* ---------- MISIONES / QUESTS ---------- */
.quest-row{ display:flex; align-items:center; gap:10px; padding:10px; border-radius:var(--r-md); background:var(--panel-soft); border:1px solid var(--stroke); }
.quest-row.ready{ border-color:var(--c-neon); box-shadow:0 0 16px rgba(57,255,136,.2); }
.quest-row .qt{ flex:1; min-width:0; }
.quest-row .qt b{ font-size:12.5px; display:block; }
.quest-row .qt .bar{ margin-top:5px; }

/* ---------- RESULTADO ---------- */
.result-banner{
  text-align:center; padding:18px 10px 10px;
}
.result-banner h2{
  margin:0; font-size:34px; letter-spacing:4px; font-weight:900;
  background:var(--g-action); -webkit-background-clip:text; background-clip:text; color:transparent;
  filter:drop-shadow(0 0 16px rgba(57,255,136,.5));
  animation:slamIn .5s var(--ease-back);
}
.result-banner h2.lose{ background:var(--g-danger); -webkit-background-clip:text; background-clip:text; filter:drop-shadow(0 0 16px rgba(255,77,109,.5)); }
@keyframes slamIn{ from{ transform:scale(2.2); opacity:0; letter-spacing:20px } }

/* ---------- ANIMACIONES DE LA BASE ---------- */

/* HUMO de chimeneas */
.v-humo{
  fill:rgba(215,235,255,.45);
  animation:humoFade 2.4s ease-out infinite;
  animation-delay:calc(var(--i, 0) * .75s);
}
@keyframes humoFade{
  0%, 100%{ opacity:0; }
  40%{ opacity:.55; }
}

/* BALIZA de las antenas */
.v-faro{ fill:#BDF3FF; }
.v-faro-halo{
  fill:#5AC8F5;
  animation:faroLate 1.9s ease-in-out infinite;
}
@keyframes faroLate{
  0%, 100%{ opacity:.15; }
  50%{ opacity:.80; }
}

/* ATERRIZAJE de un edificio recién terminado.
   Se anima `.bimg` y NO `.bslot`: el `transform` del botón es el que lo ancla a
   su parcela (`translate(-50%,-71.9%)`), y animarlo lo desanclaría. */
@keyframes edificioCae{
  0%{ transform:translateY(-190px) scale(1.06,.9); opacity:0 }
  40%{ opacity:1 }
  62%{ transform:translateY(0) scale(1.14,.82) }   /* aplasta al tocar suelo */
  78%{ transform:translateY(-9px) scale(.95,1.07) } /* rebote corto */
  100%{ transform:none }
}
.bslot.cae .bimg{ animation:edificioCae .78s var(--ease-out) both; }
/* Polvareda: un anillo que se abre en el suelo justo al impactar. */
.bslot.cae::after{
  content:""; position:absolute; left:50%; top:71.9%;
  width:66px; height:26px; margin:-13px 0 0 -33px; border-radius:50%;
  border:2px solid rgba(190,215,255,.5); pointer-events:none;
  animation:polvareda .5s var(--ease-out) .46s both;
}
@keyframes polvareda{
  from{ transform:scale(.25); opacity:.9 }
  to{ transform:scale(1.5); opacity:0 }
}

@media (prefers-reduced-motion: reduce){
  .v-humo, .v-faro-halo, .bslot.cae .bimg, .bslot.cae::after{ animation:none !important; }
  .v-humo{ opacity:.3 }
}

/* Resultado del duelo: el titular es el PORCENTAJE, no «victoria». El duelo
   termina siempre a los 75 s, así que anunciar una victoria que el jugador no
   ha conseguido —con el coloso a media vida— confunde en las dos direcciones:
   ni ha vencido a nadie, ni sabe qué se lleva. */
.result-banner h2.duelo{ color:var(--c-cian); }
.duelo-cifra{
  margin-top:2px; font-size:44px; font-weight:900; line-height:1;
  font-variant-numeric:tabular-nums;
  color:#FFD34E; text-shadow:0 0 22px rgba(255,211,78,.55);
}
.duelo-sub{ font-size:11px; letter-spacing:2px; text-transform:uppercase; color:var(--tx-lo); }

/*
 * La tira del REGALO DIARIO reutiliza `.bp-track`, pero sus fichas miden ~105
 * px de alto y no los 190 del pase de batalla: la reserva de sitio de los
 * niveles aun no vistos (`contain-intrinsic-size`, ver arriba) inflaba la fila
 * a 190 y dejaba un hueco muerto entre las fichas y el boton de reclamar —
 * reportado tal cual: «que onda ese espacio vacio en tienda». Misma
 * optimizacion, medida correcta.
 */
.bp-track.regalo .bp-tier{ contain-intrinsic-size:auto 78px auto 106px; }
