/* =========================================================================
   CAPA HOLOGRÁFICA — identidad visual sci-fi militar.

   QUÉ ES ESTO Y QUÉ NO ES.

   No redibuja la base: la isla dimétrica de `ui/basemap.js` se queda tal cual,
   porque es la identidad del juego y porque su sistema de coordenadas está
   medido (el ancla de cada `.bslot` cae en el centro de su `.mp-parcela`, con
   0 px de desfase en los doce). Lo que se añade aquí son CAPAS por debajo y
   por encima de ese dibujo, todas `pointer-events:none` y todas absolutas, de
   modo que no pueden desplazar ni un píxel de lo que ya estaba colocado.

   La regla que lo gobierna todo: PROFUNDIDAD POR CAPAS. Antes todo vivía en el
   mismo plano y por eso se leía como una ilustración plana. Ahora hay orden:

     atmósfera → terreno → caminos de energía → parcelas → edificios →
     luz → partículas → HUD

   y cada capa se mueve a su propia velocidad. Es lo que hace que la base
   parezca viva sin que el jugador toque nada.

   TODO SE APAGA con `prefers-reduced-motion`. Al final del fichero.
   ========================================================================= */

:root{
  /* --- Jerarquía semántica del color ---
     El juego ya tenía los tonos; lo que faltaba era que cada uno SIGNIFICARA
     algo siempre lo mismo, para que el jugador aprenda a leer la pantalla sin
     texto. Estos alias no inventan colores nuevos: apuntan a los tokens que ya
     existen y les ponen nombre por función. */
  --fn-tech:   var(--c-blue);      /* tecnología / información / estructura   */
  --fn-scan:   var(--gem);         /* datos, escaneo, energía en tránsito     */
  --fn-good:   var(--ok);          /* positivo, mejora disponible, producción */
  --fn-reward: var(--gold);        /* recompensa cobrable                     */
  --fn-danger: var(--bad);         /* peligro, bloqueo, sin recursos          */

  /* Velocidades de las capas atmosféricas. Distintas a propósito: si todas
     laten al mismo ritmo el conjunto se lee como un único parpadeo. */
  --sp-grid:  22s;
  --sp-scan:  7.5s;
  --sp-ring:  4.6s;
  --sp-motes: 19s;
}

/* =========================================================================
   1. ATMÓSFERA DE LA ISLA
   Siete capas por debajo del terreno. Van dentro de `.isla`, que ya es el
   contenedor posicionado de cada trozo de base.
   ========================================================================= */
.isla-atmos{
  position:absolute; inset:0;
  pointer-events:none;
  overflow:hidden;
  /* Por debajo del SVG del terreno (que no lleva z-index) y de los edificios. */
  z-index:0;
}

/* --- Capa 1 · malla ondulante (canvas) ---------------------------------
   Las dos capas que había aquí (`at-grid` y `at-hex`) eran gradientes
   repetidos desplazándose en bloque: papel pintado. Ahora es una rejilla en
   perspectiva que ondula de verdad, dibujada en `ui/isla-malla.js`.

   NO lleva animación CSS ni `filter`, y es deliberado: el canvas se repinta a
   sí mismo sin tocar el DOM, que es la única forma que aguanta el WebView de
   Android sin los parpadeos que dio esta pantalla. Cualquier animación que se
   añada aquí devuelve el problema. */
.isla-atmos .at-malla{
  position:absolute; inset:0;
  width:100%; height:100%;
  display:block;
  opacity:.85;
  /* Difumina los bordes para que la malla nazca de la nada en vez de cortarse
     contra la caja. Es una máscara estática: no se anima, no cuesta. */
  mask-image:radial-gradient(ellipse 66% 58% at 50% 46%, #000 22%, transparent 82%);
  -webkit-mask-image:radial-gradient(ellipse 66% 58% at 50% 46%, #000 22%, transparent 82%);
}

/* --- Capa 3 · barrido de escaneo ---------------------------------------
   Una banda de luz que recorre la isla. Es el elemento que más "vivo" hace el
   conjunto por lo poco que cuesta: una sola franja en movimiento. */
.isla-atmos .at-scan{
  position:absolute; left:-40%; right:-40%; height:42%;
  top:-42%;
  background:linear-gradient(180deg,
    transparent,
    rgba(85,230,255,.055) 42%,
    rgba(85,230,255,.13) 50%,
    rgba(85,230,255,.055) 58%,
    transparent);
  filter:blur(1px);
  animation:atScan var(--sp-scan) linear infinite;
}
@keyframes atScan{ to{ transform:translate3d(0, 340%, 0); } }

/* --- Capa 4 · anillos holográficos concéntricos ------------------------
   Nacen en el centro de la isla y se expanden. Marcan el punto de mando como
   origen de todo antes incluso de mirar los edificios. */
.isla-atmos .at-ring{
  position:absolute; left:50%; top:52%;
  width:34%; aspect-ratio:2 / 1;              /* 2:1 = la dimétrica del suelo */
  margin:0; translate:-50% -50%;
  border:1px solid rgba(85,230,255,.34);
  border-radius:50%;
  opacity:0;
  animation:atRing var(--sp-ring) cubic-bezier(.16,1,.3,1) infinite;
}
.isla-atmos .at-ring:nth-child(2){ animation-delay:1.5s; }
.isla-atmos .at-ring:nth-child(3){ animation-delay:3s; }
@keyframes atRing{
  0%   { transform:scale(.28); opacity:0; }
  18%  { opacity:.55; }
  100% { transform:scale(2.4); opacity:0; }
}

/* --- Capa 5 · motas en suspensión --------------------------------------
   Partículas de verdad, pero baratas: nodos con `animation`, no un sistema con
   su bucle. Un sistema de partículas para doce puntos en una pantalla de menú
   es pagar un motor entero por nada (el pooling está donde importa, que es el
   combate). */
.isla-atmos .at-mote{
  position:absolute;
  width:2px; height:2px; border-radius:50%;
  background:rgba(168,255,203,.85);
  box-shadow:0 0 6px rgba(57,255,136,.75);
  animation:atMote var(--sp-motes) linear infinite;
}
@keyframes atMote{
  0%   { transform:translate3d(0,10px,0); opacity:0; }
  12%  { opacity:.9; }
  88%  { opacity:.65; }
  100% { transform:translate3d(14px,-84px,0); opacity:0; }
}

/* --- Capa 6 · neblina inferior ----------------------------------------- */
.isla-atmos .at-fog{
  position:absolute; left:-20%; right:-20%; bottom:-12%; height:46%;
  background:radial-gradient(ellipse 50% 100% at 50% 100%,
    rgba(46,107,255,.20), transparent 70%);
  filter:blur(6px);
  animation:atFog 13s ease-in-out infinite alternate;
}
@keyframes atFog{ from{ opacity:.5; transform:translate3d(-2%,0,0) } to{ opacity:.95; transform:translate3d(2%,0,0) } }

/* --- Capa 7 · viñeta de profundidad ------------------------------------
   Va POR ENCIMA del terreno pero por debajo de los edificios: hunde los bordes
   de la isla y empuja el centro hacia el ojo. Es lo que separa el plano del
   suelo del plano de los volúmenes. */
.isla-vineta{
  position:absolute; inset:0; pointer-events:none; z-index:1;
  /* La elipse sigue la SILUETA de la isla (1,92:1 dentro de la caja cuadrada
     del suelo), no un óvalo genérico: así el oscurecido cae sobre el agua y no
     sobre la fila de edificios de abajo, que es lo que pasaba con 62% x 52%. */
  background:radial-gradient(ellipse 60% 31% at 50% 52%,
    transparent 46%, rgba(3,5,14,.50) 100%);
}

/* =========================================================================
   2. CAMINOS COMO LÍNEAS DE ENERGÍA
   La red de caminos ya existía y ya salía del nudo correcto (el Centro de
   Mando). Lo que le faltaba era CORRER: una calzada quieta se lee como un
   dibujo, y una con flujo dice que la instalación está funcionando.
   ========================================================================= */
.mp-camino-flujo{
  fill:none;
  stroke-width:.85;
  stroke-linecap:round;
  stroke-dasharray:3 15;
  opacity:.9;
  animation:flujoEnergia 1.6s linear infinite;
}
@keyframes flujoEnergia{ to{ stroke-dashoffset:-18; } }

/* Cada camino arranca con su propio desfase: sin escalonar, los pulsos salen
   todos a la vez desde el núcleo y se lee como un parpadeo, no como flujo. */
.mp-camino-flujo:nth-of-type(2n){ animation-delay:-.45s; }
.mp-camino-flujo:nth-of-type(3n){ animation-delay:-.9s; }

/* =========================================================================
   3. EL NÚCLEO
   El Centro de Mando deja de ser "un edificio más de la lista" y pasa a ser el
   corazón visual de la base: emite una onda cada pocos segundos y las líneas
   de energía nacen de él.
   ========================================================================= */
.bslot.es-nucleo .bimg::after{
  content:"";
  position:absolute; left:50%; top:72%;
  width:130%; aspect-ratio:2 / 1;
  translate:-50% -50%;
  border:1.5px solid rgba(57,255,136,.5);
  border-radius:50%;
  pointer-events:none;
  animation:corePulse 3.6s cubic-bezier(.16,1,.3,1) infinite;
}
@keyframes corePulse{
  0%   { transform:scale(.5);  opacity:0; }
  22%  { opacity:.75; }
  100% { transform:scale(1.9); opacity:0; }
}

/* =========================================================================
   4. ESTADO DE CADA EDIFICIO
   Un punto de color y una palabra. Permite entender la base de un vistazo sin
   abrir doce fichas — que era justo lo que obligaba a hacer antes.

   VA ABSOLUTO Y FUERA DEL FLUJO, como el rótulo. La caja del `.bslot` mide
   exactamente lo que el edificio, y meter aquí un elemento en flujo
   desplazaría el dibujo respecto a su parcela: es el fallo que ya se midió una
   vez (13 px de desfase en los doce edificios).
   ========================================================================= */
.bstate{
  position:absolute; left:50%; top:22px;
  translate:-50% -100%;
  display:flex; align-items:center; gap:4px;
  padding:2px 7px 2px 5px;
  border-radius:99px;
  font-size:8.5px; font-weight:800; letter-spacing:.08em;
  white-space:nowrap;
  color:var(--tx-mid);
  background:rgba(8, 14, 30, 0.68);
  border:1px solid rgba(150, 190, 255, 0.24);
  backdrop-filter:blur(5px); -webkit-backdrop-filter:blur(5px);
  box-shadow:0 2px 6px rgba(0,0,0,.45);
  pointer-events:none;
  z-index:10;
}

.bstate i{
  width:5px; height:5px; border-radius:50%;
  background:currentColor;
  font-style:normal;
  animation:bstateBlink 2.2s ease-in-out infinite;
}
@keyframes bstateBlink{ 0%,100%{ opacity:1 } 50%{ opacity:.35 } }

.bstate.st-prod  { color:var(--fn-good); background:rgba(4, 24, 14, 0.68); border-color:rgba(57, 255, 136, 0.35); }
.bstate.st-work  { color:var(--fn-reward); background:rgba(28, 20, 4, 0.68); border-color:rgba(255, 197, 61, 0.35); }
.bstate.st-tech  { color:var(--fn-scan); background:rgba(4, 20, 28, 0.68); border-color:rgba(85, 230, 255, 0.35); }
.bstate.st-up    { color:var(--fn-good); background:rgba(4, 24, 14, 0.68); border-color:var(--stroke-hot); }
.bstate.st-idle  { color:var(--tx-lo); background:rgba(8, 14, 30, 0.68); }
.bstate.st-idle i{ animation:none; }
.bstate.st-lock  { color:var(--fn-danger); background:rgba(28, 4, 8, 0.68); border-color:rgba(255, 77, 106, 0.35); }
.bstate.st-broken{ color:#ff5577; background:rgba(38, 6, 12, 0.85); border-color:rgba(255, 59, 92, 0.6); box-shadow:0 0 10px rgba(255,59,92,0.4); }

@keyframes pvpFadeIn{ from{ opacity:0; transform:scale(1.04); } to{ opacity:1; transform:scale(1); } }
@keyframes pvpPulse{ 0%{ transform:scale(1); opacity:0.85; } 100%{ transform:scale(1.15); opacity:1; } }
@keyframes pvpSpin{ from{ transform:rotate(0deg); } to{ transform:rotate(360deg); } }

/* =========================================================================
   4b. LOS ORBES SIEMPRE POR ENCIMA
   Fallo real, reportado: los orbes de producción no se podían tocar.

   Causa medida con `elementFromPoint`: el orbe se posa 11 % por encima del
   ancla de su edificio, justo sobre el cuerpo del volumen, y cada `.bslot`
   lleva `z-index` = la `y` de su parcela (hasta 68) para el orden de pintado
   por profundidad. La capa de orbes iba sin `z-index`, así que por mucho que
   fuese la última del DOM el edificio la tapaba y se quedaba con el toque.

   No se toca el `z-index` de los edificios: ese es el que sostiene la ilusión
   de volumen (el de delante tapa al de detrás). Se sube la capa de orbes por
   encima de todos ellos, que además es lo correcto — un orbe es una recompensa
   que hay que poder coger, no parte del escenario.
   ========================================================================= */
.orb-layer{
  /*
   * MISMA CAJA CUADRADA QUE `.isla-suelo`, porque las posiciones de los orbes
   * son coordenadas de mapa: si la caja no fuese idéntica, el orbe dejaría de
   * caer sobre su edificio. La capa cuelga ahora del paginador y no del suelo
   * (ver `screens/base.js`), así que tiene que replicar esa geometría en vez de
   * heredarla.
   */
  position:absolute; left:50%; width:100%; aspect-ratio:1 / 1;
  top:50%; translate:-50% -50%;
  transform-origin:50% 55%;
  /*
   * 200 y no 20: aquí sí sirve de algo. Colgando del paginador —que no crea
   * contexto de apilamiento— este número compite directamente con el de los
   * atajos de la esquina, que van a 12. Dentro de `.isla-suelo` competía sólo
   * contra sus hermanos y perdía contra cualquier botón de `.base-view`.
   */
  z-index:200;
  /* La capa no intercepta nada; cada orbe sí. Sin esto, una lámina invisible
     sobre toda la isla dejaría los edificios sin poder tocarse. */
  pointer-events:none;
}
.orb-layer .orb{ pointer-events:auto; }
/*
 * Se aparta durante el arrastre entre islas. La capa es de la isla activa y no
 * viaja con la tira, así que mientras el dedo desliza se quedaría flotando
 * sobre la isla equivocada. Al soltar, `refresh()` la reconstruye para la que
 * toque.
 */
.isla-pager.arrastrando > .orb-layer{ opacity:0; pointer-events:none; }
.orb-layer{ transition:opacity .16s var(--ease-out); }

/* =========================================================================
   4c. EL PREDIO: terreno con materia
   El suelo era una mancha con un degradado y una rejilla. Ahora tiene canto de
   roca, filo de costa, señalización y plataformas con grosor. Todo estático:
   ni una de estas reglas anima nada, así que el coste es pintarlo una vez.
   ========================================================================= */

/* Filo de la costa. Fino y encendido: es el remate que separa la tierra del
   vacío. Va sin relleno para no volver a pintar los 100x100 del suelo. */
.mp-costa{
  fill:none;
}

/* Contacto de la plataforma con el terreno. Difusa y oscura: es lo que impide
   que el edificio parezca pegado por encima en lugar de apoyado. */
.mp-ocluye{ fill:rgba(2,4,10,.42); }

/* Canto de la losa: el grosor de la plataforma. */
.mp-canto{ fill:rgba(4,7,16,.85); }

/* Cara superior. El relleno lo pone el degradado de la isla (en `style`), así
   que aquí sólo queda el trazo tenue que la cierra. */
.mp-parcela{ stroke:rgba(150,190,255,.10); stroke-width:.25; }

/* Filo encendido de la plataforma, con el acento de cada isla. */
.mp-filo{
  fill:none;
  stroke-width:.42;
  stroke-opacity:.55;
}

/* Señalización del suelo. Muy tenue a propósito: tiene que sentirse, no leerse
   — en cuanto se distingue como "unos rombos", compite con los edificios. */
.mp-marca{ fill:rgba(150,190,255,.05); stroke:rgba(150,190,255,.10); stroke-width:.22; }

/* =========================================================================
   4d. VOLÚMENES CON MATERIA
   El sombreado plano de tres tonos NO se toca: es lo que hace que un cuerpo se
   lea de un vistazo, y está documentado como decisión. Lo que se añade son las
   dos cosas que le faltaban para no parecer una maqueta — el canto de la luz y
   las ventanas encendidas.
   ========================================================================= */

/* Arista superior iluminada. Es lo que le da CANTO al material: sin ella dos
   caras planas comparten un borde muerto y el cuerpo se lee como cartulina. */
.v-remate{
  fill:none;
  stroke:rgba(190,225,255,.42);
  stroke-width:.4;
  stroke-linecap:round;
  stroke-linejoin:round;
}
.v-remate.v-acento{ stroke:rgba(180,245,255,.75); stroke-width:.5; }

/* Ventanas. Diminutas y cálidas contra el azul del cuerpo: es el contraste de
   temperatura lo que las hace leer como luz interior y no como manchas. */
.v-ventana{
  fill:#8FE6FF;
  fill-opacity:.72;
  stroke:none;
}
.v-ventana.off{ fill:#0E1730; fill-opacity:.55; }

/* Perímetro de la instalación (cuarto tramo, nivel 40+). Marca el recinto
   entero como propiedad del edificio: es la señal de que ya no es una obra,
   es un complejo. */
.v-perimetro{
  fill:none;
  stroke:rgba(140,190,255,.24);
  stroke-width:.5;
  stroke-dasharray:3 2.4;
}

/* =========================================================================
   4e. FONDO VARIABLE POR ISLA
   Cada sector trae su propia paleta (`config/buildings.js -> ISLAS.paleta`) y
   la pantalla la recibe en variables. Sin esto las dos islas compartían fondo y
   avanzar de sector no se notaba: el mismo sitio con otro rótulo.
   ========================================================================= */
.isla{
  /* Reserva por si una isla no declara paleta. */
  --isla-cielo-0:#0A1330;
  --isla-cielo-1:#050912;
  --isla-brillo:#2E6BFF;
  /* RESERVA PRIMERO. `color-mix` no existe en WebViews de Android antiguos, y
     si el navegador no entiende una declaración la descarta ENTERA — con el
     degradado dentro. Sin esta línea previa, esos aparatos se quedarían sin
     fondo de sector. Con ella pierden el halo y conservan el cielo, que es lo
     que de verdad distingue una isla de otra. */
  background:linear-gradient(180deg, var(--isla-cielo-0) 0%, var(--isla-cielo-1) 100%);
  background:
    radial-gradient(72% 46% at 50% 42%, color-mix(in srgb, var(--isla-brillo) 22%, transparent), transparent 70%),
    linear-gradient(180deg, var(--isla-cielo-0) 0%, var(--isla-cielo-1) 100%);
}

/* =========================================================================
   4e-bis. EL CIELO SIGUE A LA CAMPAÑA
   `screens/base.js` inyecta el bioma del sector en el que combate el jugador.
   Reserva primero, por el mismo motivo de siempre: si las variables no
   estuvieran puestas, la pantalla se quedaría sin fondo.
   ========================================================================= */
.base-view{
  background:
    radial-gradient(90% 55% at 50% 6%,
      color-mix(in srgb, var(--cielo-acento, #55E6FF) 16%, transparent), transparent 62%),
    radial-gradient(80% 60% at 50% 96%,
      color-mix(in srgb, var(--cielo-niebla, #0E2038) 70%, transparent), transparent 70%),
    linear-gradient(180deg,
      var(--cielo-0, #070B18) 0%,
      var(--cielo-1, #0C1226) 58%,
      var(--cielo-niebla, #101A38) 100%);
}

/* =========================================================================
   4f. OPTIMIZACIÓN: SÓLO SE ANIMA LO QUE SE MIRA
   Las islas viven todas a la vez en una tira horizontal, así que el compositor
   sigue moviendo las motas, el barrido, los anillos y la neblina de las que
   están fuera del encuadre. Con dos islas es el doble del trabajo visible.

   Se congelan las inactivas. `animation-play-state` no reinicia nada: al
   volver, la animación continúa donde estaba, así que el jugador no ve ningún
   salto. Durante el arrastre se reanudan todas, porque ahí sí se ven dos.
   ========================================================================= */
.isla:not(.activa) .isla-atmos,
.isla:not(.activa) .mp-camino-flujo,
.isla:not(.activa) .v-humo,
.isla:not(.activa) .v-faro-halo,
.isla:not(.activa) .worker-track,
.isla:not(.activa) .wb-robot,
.isla:not(.activa) .wb-ground-bot,
.isla:not(.activa) .wb-jet-flames,
.isla:not(.activa) .wb-visor,
.isla:not(.activa) .wb-scan-cone,
.isla:not(.activa) .wb-welder-spark,
.isla:not(.activa) .drone-blade,
.isla:not(.activa) .wb-drill-head,
.isla:not(.activa) .wb-drill-spark,
.isla:not(.activa) .wb-shield-field,
.isla:not(.activa) .wb-orbit-ring-1,
.isla:not(.activa) .wb-orbit-ring-2,
.isla:not(.activa) .wb-prism-spin,
.isla:not(.activa) .wb-void-disk,
.isla:not(.activa) .wb-orbit-orb-1,
.isla:not(.activa) .wb-orbit-orb-2,
.isla:not(.activa) .wb-orbit-orb-3{
  animation-play-state:paused !important;
}

/* La atmósfera toma el acento del sector: el barrido, los anillos y las motas
   dejan de ser cian fijo y pasan a ser del color de la isla. (La malla lo hace
   por su cuenta: lee `--isla-acento` al montarse, en `isla-malla.js`.) */
.isla-atmos .at-scan{
  background:linear-gradient(180deg,
    transparent,
    color-mix(in srgb, var(--isla-acento, #55E6FF) 8%, transparent) 42%,
    color-mix(in srgb, var(--isla-acento, #55E6FF) 18%, transparent) 50%,
    color-mix(in srgb, var(--isla-acento, #55E6FF) 8%, transparent) 58%,
    transparent);
}
.isla-atmos .at-ring{ border-color:color-mix(in srgb, var(--isla-acento, #55E6FF) 42%, transparent); }
.isla-atmos .at-fog{
  background:radial-gradient(ellipse 50% 100% at 50% 100%,
    color-mix(in srgb, var(--isla-brillo, #2E6BFF) 24%, transparent), transparent 70%);
}

/* =========================================================================
   4g. DOPAMINA: LA OBRA TERMINADA SE NOTA EN TODO EL SECTOR
   Sube un edificio y el suelo responde. Sólo `transform` y `opacity`, que es lo
   único que el compositor puede animar sin repintar, y el nodo se retira solo.
   ========================================================================= */
.onda-obra{
  position:absolute;
  width:26%; aspect-ratio:2 / 1;        /* 2:1 = la dimétrica del terreno */
  translate:-50% -50%;
  border-radius:50%;
  /* Reserva antes del `color-mix`, por el mismo motivo que en `.isla`: sin ella
     un WebView antiguo se quedaría sin borde y la onda sería invisible. */
  border:1.5px solid rgba(85,230,255,.85);
  box-shadow:0 0 22px rgba(85,230,255,.45);
  border-color:color-mix(in srgb, var(--isla-acento, #55E6FF) 85%, transparent);
  box-shadow:0 0 22px color-mix(in srgb, var(--isla-acento, #55E6FF) 45%, transparent);
  pointer-events:none;
  z-index:120;
  opacity:0;
  animation:ondaObra 1.1s cubic-bezier(.16,1,.3,1) forwards;
}
.onda-obra.grande{
  border-color:rgba(255,211,78,.95);
  box-shadow:0 0 30px rgba(255,211,78,.6);
  animation-duration:1.5s;
}
@keyframes ondaObra{
  0%  { transform:scale(.14); opacity:0; }
  16% { opacity:.95; }
  100%{ transform:scale(4.2); opacity:0; }
}

/* Un salto de tramo cambia la SILUETA del edificio: el sector entero lo acusa
   con un golpe de luz muy corto. Largo se leería como un fallo de render. */
.isla.destello{
  animation:islaDestello .56s var(--ease-out);
  /*
   * CAPA PROPIA MIENTRAS DURA EL DESTELLO. `islaDestello` anima `filter` sobre
   * LA ISLA ENTERA — el elemento mas grande de la pantalla de base—, y un
   * filtro animado re-rasteriza en cada fotograma. Sin capa propia esa
   * invalidacion baja al arbol completo y en el WebView se ve como el
   * parpadeo de toda la pantalla al subir de nivel. Es EXACTAMENTE el fallo ya
   * corregido en `.bimg.evoluciona`; este era su hermano mayor y quedo sin
   * tocar. Promovido, el coste queda confinado a la textura de la isla.
   */
  will-change:filter;
  isolation:isolate;
}
@keyframes islaDestello{
  0%,100%{ filter:none; }
  30%    { filter:brightness(1.42) saturate(1.25); }
}

/* El número de nivel del edificio da un salto al cambiar. Es el dato que el
   jugador estaba persiguiendo; si aparece sin más, la mejora no se cobra. */
.bslot.cae .blvl{ animation:nivelPop .5s var(--ease-back) .52s backwards; }
@keyframes nivelPop{
  0%  { transform:scale(0) rotate(-25deg); opacity:0; }
  60% { transform:scale(1.45) rotate(6deg); opacity:1; }
  100%{ transform:scale(1); }
}

/* =========================================================================
   4h. EVOLUCIÓN: LA LUZ RECORRE EL EDIFICIO
   Antes una subida de nivel era un destello DELANTE de la pieza. Ahora la
   energía la atraviesa: el cuerpo se enciende de abajo arriba con su propio
   color de material (`--ev-luz`, lo pone `screens/base.js`).

   Se anima `.bimg` y NO `.bslot`, porque el `transform` del botón es el que lo
   ancla a su parcela — animarlo lo desanclaría.
   ========================================================================= */
.bslot .bimg{
  /* Capa viva desde el primer pintado: ver la nota del barrido de abajo. Son
     ~12 texturas de ~150 px — coste fijo minusculo a cambio de cero fogonazos. */
  will-change:filter;
  isolation:isolate;
}
.bslot .bimg.evoluciona{
  animation:evolucionaPieza .9s var(--ease-out);
  /*
   * CAPA PROPIA MIENTRAS DURA. Un `filter` animado re-rasteriza el elemento en
   * CADA fotograma; sin esto la invalidacion no se queda en el edificio sino
   * que baja al arbol de la isla entera, y en el WebView de Android eso se ve
   * como un parpadeo de toda la pantalla cada vez que algo sube de nivel —
   * reportado tal cual. Promovido, el repintado queda confinado a la textura
   * del propio dibujo (~150 px): mismo efecto, coste local.
   */
  will-change:filter;
  isolation:isolate;
}
@keyframes evolucionaPieza{
  0%   { filter:none; }
  18%  { filter:brightness(1.9) saturate(1.5)
                drop-shadow(0 0 10px var(--ev-luz, #55E6FF)); }
  45%  { filter:brightness(1.35) saturate(1.25)
                drop-shadow(0 0 18px var(--ev-luz, #55E6FF)); }
  100% { filter:none; }
}

/* Barrido: una banda de luz que sube por el cuerpo. Va en un pseudo-elemento
   recortado al cuadro del dibujo, así no mancha lo que tiene alrededor. */
/*
 * EL BARRIDO PRE-EXISTE, dormido con opacidad 0, y `.evoluciona` solo lo
 * anima. Antes el pseudo-elemento —con su mezcla y su capa— NACIA en el mismo
 * fotograma en que empezaba la animacion, y en el WebView una capa compuesta
 * recien creada puede ensenarse un fotograma SIN rasterizar: ese es el
 * «parpadeo blanco potente» reportado al subir de nivel. Una capa que ya
 * existe no tiene fotograma de nacimiento.
 */
.bslot .bimg::before{
  content:"";
  position:absolute; inset:0;
  pointer-events:none;
  opacity:0;
  background:linear-gradient(0deg,
    transparent 0%,
    var(--ev-luz, #55E6FF) 45%,
    transparent 60%);
  /*
   * LA MEZCLA `screen` VUELVE — pero en capa propia del compositor.
   *
   * Historia completa, porque este bloque ya dio dos vueltas:
   *  1. La mezcla sin capa repintaba la isla entera por fotograma (el
   *     parpadeo original).
   *  2. Se quito la mezcla a secas... y la banda dejo de ser LUZ para ser
   *     PINTURA: un degradado plano al 62 % sobre la caja del dibujo se ve
   *     como un rectangulo del color del edificio barriendo la pantalla —
   *     reportado tal cual: «se pinta un cuadro rojo por unas milesimas» (el
   *     arsenal tiene la luz roja).
   *
   * La salida buena es la tercera: mezcla SI, pero con el elemento promovido a
   * su propia capa (`will-change` + `translateZ`). Asi el compositor resuelve
   * la mezcla contra su textura de fondo sin repintar el arbol, y la banda
   * vuelve a verse como luz que recorre la pieza — identica al diseño
   * original, con el coste confinado.
   */
  mix-blend-mode:screen;
  will-change:transform,opacity;
  transform:translateZ(0);
}
.bslot .bimg.evoluciona::before{
  animation:evolucionaBarrido .9s var(--ease-out);
}
@keyframes evolucionaBarrido{
  from{ transform:translateY(60%); opacity:0; }
  35% { opacity:.55; }
  to  { transform:translateY(-90%); opacity:0; }
}

/* =========================================================================
   4i. PANELES CON VIDA
   Un panel estático es un rectángulo. Con un filo que se enciende al entrar, se
   lee como una superficie con energía detrás.

   Sólo al ENTRAR en la pantalla, no en bucle: un brillo que da vueltas sin
   parar cansa a los diez segundos y compite con lo que el jugador quiere leer.
   ========================================================================= */
.stagger > .panel{
  position:relative;
  overflow:hidden;
}
.stagger > .panel::after{
  display: none !important;
}

/* =========================================================================
   5. FEED DE ACTIVIDAD TÁCTICA
   Avisos que aparecen y se van solos. NO son toasts del sistema: son
   hologramas: entran deslizándose desde la izquierda, con su barra de acento y
   su tipografía de datos. Sirven para que la base cuente lo que hace.
   ========================================================================= */
.holo-feed{
  position:absolute; left:10px; right:10px; top:78px;
  display:flex; flex-direction:column; gap:5px;
  pointer-events:none;
  z-index:6;
}
.holo-line{
  align-self:flex-start;
  max-width:100%;
  display:flex; align-items:center; gap:7px;
  padding:5px 11px 5px 8px;
  border-radius:4px 12px 12px 4px;
  background:linear-gradient(90deg, rgba(9,14,30,.9), rgba(9,14,30,.42));
  border-left:2px solid var(--fn-scan);
  box-shadow:0 4px 18px rgba(0,0,0,.5);
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  font-size:10.5px; font-weight:700; letter-spacing:.05em;
  color:var(--tx-mid);
  text-transform:uppercase;
  animation:holoIn .34s var(--ease-out), holoOut .4s var(--ease-out) 3.4s forwards;
}
.holo-line b{ color:var(--tx-hi); font-weight:800; }
.holo-line .hi{ font-size:12px; display:grid; place-items:center; }
.holo-line.tono-good  { border-left-color:var(--fn-good); }
.holo-line.tono-reward{ border-left-color:var(--fn-reward); }
.holo-line.tono-tech  { border-left-color:var(--fn-scan); }
.holo-line.tono-alert { border-left-color:var(--fn-danger); }

@keyframes holoIn{
  from{ opacity:0; transform:translate3d(-16px,0,0) skewX(-8deg); }
  to  { opacity:1; transform:none; }
}
@keyframes holoOut{
  to{ opacity:0; transform:translate3d(0,-6px,0); }
}

/* =========================================================================
   6. EL BOTÓN DE ASALTO
   Es el botón más importante de la pantalla y era un rectángulo verde igual
   que los demás. Ahora domina: gradiente propio, filo encendido, reflejo que
   lo atraviesa y latido lento.

   El latido es LENTO (3,2 s) a propósito: un botón que parpadea rápido se lee
   como una alerta y cansa a los diez segundos de tenerlo delante.
   ========================================================================= */
.btn-asalto{
  position:relative;
  overflow:hidden;
  isolation:isolate;
  padding:15px 18px !important;
  border-radius:var(--r-lg) !important;
  background:linear-gradient(135deg, #0E7C46 0%, #12A455 45%, #39FF88 100%) !important;
  border:1px solid rgba(168,255,203,.55) !important;
  box-shadow:0 10px 34px rgba(18,164,85,.42), inset 0 1px 0 rgba(255,255,255,.28);
}
/*
 * AQUÍ HABÍA UN `@keyframes` QUE ANIMABA `box-shadow`, y más abajo otro que
 * animaba `filter:drop-shadow`. Los dos sobre el MISMO botón, que además está
 * en pantalla de forma permanente.
 *
 * Ninguna de las dos propiedades la puede resolver el compositor: cada
 * fotograma es un repintado del botón y de su halo, sesenta veces por segundo,
 * para siempre. En el WebView del APK eso es presupuesto de rasterizado que se
 * está gastando de fondo aunque el jugador no esté mirando el botón.
 *
 * El latido ahora lo hace `.btn-asalto::after` (ver más abajo) con `opacity`,
 * que sí es composición pura.
 */

/* Reflejo que cruza el botón. Va en un pseudo-elemento con `mix-blend-mode`
   para que no aclare el texto: sobre el rótulo, un barrido blanco a secas lo
   deja ilegible durante el paso. */
.btn-asalto::before{
  content:"";
  position:absolute; top:0; bottom:0; left:-60%;
  width:45%;
  background:linear-gradient(100deg, transparent, rgba(255,255,255,.42), transparent);
  transform:skewX(-18deg);
  z-index:-1;
  animation:asaltoBrillo 4.4s ease-in-out infinite;
}
/* Por `transform` y no por `left` — ver la nota de `sheen` en components.css.
   Éste está en el botón de asalto, que se ve siempre: animaba el maquetado del
   botón 60 veces por segundo mientras el jugador leía el panel.
   190% del padre sobre un elemento del 45% = 422,22% de su propio ancho. */
@keyframes asaltoBrillo{
  0%,62%  { transform:translateX(0) skewX(-18deg); }
  92%,100%{ transform:translateX(422.22%) skewX(-18deg); }
}

/*
 * Rejilla táctica sobre el propio botón: lo despega de "botón de formulario".
 * Y ADEMÁS es quien late: lleva un resplandor verde encima de la rejilla y se
 * le anima la OPACIDAD, que el compositor resuelve sin repintar nada. El
 * resultado en pantalla es el mismo respirar que antes costaba dos repintados
 * por fotograma.
 */
.btn-asalto::after{
  content:"";
  position:absolute; inset:0; z-index:-1;
  background-image:
    radial-gradient(120% 140% at 50% 120%, rgba(168,255,203,.42), transparent 62%),
    linear-gradient(90deg, rgba(255,255,255,.10) 1px, transparent 1px);
  background-size:100% 100%, 9px 100%;
  opacity:.35;
  animation:asaltoLatidoOpacidad 3.2s ease-in-out infinite;
}
@keyframes asaltoLatidoOpacidad{
  0%,100%{ opacity:.32; }
  50%    { opacity:.72; }
}

.btn-asalto .as-txt{
  display:flex; flex-direction:column; align-items:center; gap:1px;
  line-height:1.12;
}
.btn-asalto .as-main{
  font-size:17px; font-weight:900; letter-spacing:.14em;
  text-shadow:0 2px 8px rgba(0,0,0,.45);
}
.btn-asalto .as-sub{
  font-size:9.5px; font-weight:700; letter-spacing:.16em;
  opacity:.82;
}
.btn-asalto:active{ transform:scale(.975); }

/* =========================================================================
   7. SECUENCIA DE LANZAMIENTO
   Ir de la base al combate era `display:none` / `display:block`, que es lo que
   mata la sensación de producto acabado. Ahora: el núcleo se enciende, lo
   secundario se apaga, la cámara empuja hacia dentro y un destello entrega la
   pantalla al motor de misión.

   Dura 620 ms y NO bloquea nada: el motor arranca al terminar, así que si algo
   fallara aquí el juego seguiría entrando en combate igual.
   ========================================================================= */
.lanzando .base-view{
  animation:lanzarBase .62s cubic-bezier(.16,1,.3,1) forwards;
  /* Anima transform + filter: capa propia o repinta la pantalla (ver
     `.isla.destello`, el mismo caso). */
  will-change:transform, filter, opacity;
  transform-origin:50% 46%;
}
@keyframes lanzarBase{
  0%   { transform:scale(1);    filter:brightness(1) saturate(1); }
  38%  { transform:scale(1.07); filter:brightness(1.5) saturate(1.35); }
  100% { transform:scale(1.9);  filter:brightness(2.4) saturate(1.6); opacity:0; }
}
/* Lo secundario se retira antes que el mapa: si todo se va a la vez el ojo no
   sabe qué mirar y el efecto se pierde. */
.lanzando .hero-cta,
.lanzando .isla-nav,
.lanzando .holo-feed{
  animation:lanzarSecundario .26s var(--ease-out) forwards;
}
@keyframes lanzarSecundario{ to{ opacity:0; transform:translate3d(0,14px,0); } }

/*
 * Destello de entrega. Nace en el núcleo, no en el centro geométrico.
 *
 * REHECHO EN EL IDIOMA DE LA MARCA. La primera versión era un lavado verde
 * menta a opacidad .92 cubriendo la pantalla entera — con la identidad del
 * juego (morado/azul sobre negro, verde SOLO como acento de acción) aquello
 * se leía como un fallo gráfico, no como una transición. Ahora es un pulso
 * frío: viñeta que oscurece los bordes hacia el negro de la casa, un halo
 * morado/azul tenue en el núcleo (opacidades .18-.35, nunca un muro de
 * color) y un anillo de onda expansiva con filo violeta, que es el gesto
 * «tecnología» de todo el HUD. El pico global baja de .92 a .8 sobre capas
 * mucho más transparentes: se siente el despegue sin teñir la pantalla.
 */
.launch-flash{
  position:fixed; inset:0; z-index:8000;
  pointer-events:none;
  background:
    radial-gradient(circle at 50% 46%,
      rgba(196,166,255,.35) 0%, rgba(139,92,246,.22) 20%, rgba(46,107,255,.14) 44%, transparent 68%),
    radial-gradient(circle at 50% 46%, transparent 42%, rgba(5,7,15,.88) 100%);
  opacity:0;
  animation:launchFlash .58s var(--ease-out) forwards;
}
/* Onda expansiva: un aro con filo violeta que crece desde el núcleo. */
.launch-flash::after{
  content:""; position:absolute; left:50%; top:46%;
  width:130vmax; height:130vmax; border-radius:50%;
  border:2px solid rgba(196,166,255,.6);
  box-shadow:0 0 26px rgba(139,92,246,.5), inset 0 0 30px rgba(46,107,255,.35);
  transform:translate(-50%,-50%) scale(.04);
  opacity:.9;
  animation:launchAnillo .58s var(--ease-out) forwards;
}
@keyframes launchFlash{
  0%  { opacity:0; transform:scale(.5); }
  42% { opacity:.8; }
  100%{ opacity:0; transform:scale(1.6); }
}
@keyframes launchAnillo{
  to{ transform:translate(-50%,-50%) scale(1); opacity:0; }
}

/* =========================================================================
   8. NAVEGACIÓN: EL ATAQUE ES EL CORAZÓN
   `NAV` ya declaraba `big:true` en la pestaña de Atacar y nadie lo leía. Se
   honra: el botón central sube, crece y se enciende.
   ========================================================================= */
.nav-item.big{
  position:relative;
  /*
   * `.nav-item` lleva `overflow:hidden` para que la etiqueta larga no desborde,
   * pero el círculo de Atacar sale 18px POR ENCIMA de la pestaña con un margen
   * negativo — y ese recorte se lo comía por arriba, dejándolo cortado en plano.
   * Aquí se libera: la etiqueta ya se recorta ella sola en `> span:last-child`,
   * así que el `overflow` del padre no hacía falta para esta pestaña.
   */
  overflow:visible;
}
.nav-item.big .nico{
  width:46px; height:46px;
  margin-top:-18px;
  display:grid; place-items:center;
  border-radius:50%;
  font-size:20px;
  color:#04140A;
  background:linear-gradient(135deg, #12A455, #39FF88);
  border:2px solid rgba(5,7,15,.9);
  position:relative;
  box-shadow:0 6px 20px rgba(18,164,85,.55), 0 0 0 3px rgba(57,255,136,.14);
}
/*
 * EL LATIDO, POR `opacity` Y NO POR `box-shadow`.
 *
 * Esto está en la barra de navegación: se ve SIEMPRE, en todas las pantallas.
 * Animar `box-shadow` obliga a repintar el elemento y su halo en cada
 * fotograma, para siempre — y `opacity` la resuelve el compositor sin repintar.
 *
 * El aspecto se conserva porque no se sustituye una sombra por otra: la débil
 * se queda fija en el elemento (arriba) y la fuerte vive aquí, apareciendo y
 * desapareciendo encima. El ojo ve el mismo latido.
 *
 * `inset:0` + `border-radius:inherit` para que el halo siga exactamente la
 * silueta circular del icono, y `pointer-events:none` para no comerse el toque
 * de la pestaña.
 */
.nav-item.big .nico::after{
  content:""; position:absolute; inset:0;
  border-radius:inherit; pointer-events:none;
  box-shadow:0 8px 26px rgba(57,255,136,.70), 0 0 0 5px rgba(57,255,136,.18);
  animation:navBig 3.2s ease-in-out infinite;
}
@keyframes navBig{ 0%,100%{ opacity:0 } 50%{ opacity:1 } }
.nav-item.big.active .nico{ transform:translateY(-1px); }

/* =========================================================================
   8b. BOTONES: CHAPA BISELADA CON LUZ
   =========================================================================

   El botón era un rectángulo redondeado con un degradado. Correcto y sin
   carácter: el mismo que trae cualquier interfaz por defecto, en un juego cuya
   identidad entera es angular y militar.

   TRES CAPAS, y cada una hace un trabajo distinto:

     · La FORMA. `clip-path` con las cuatro esquinas cortadas en chaflán. Es lo
       que convierte el botón en una pieza de chapa recortada en vez de una
       pastilla. Se hace con recorte y no con bordes porque un borde no puede
       cortar una esquina en diagonal.
     · El RELIEVE. Luz arriba y sombra abajo por dentro, para que la chapa tenga
       canto. El resplandor exterior va con `filter: drop-shadow`, no con
       `box-shadow`: `clip-path` recorta las sombras exteriores, así que un
       `box-shadow` normal desaparecería — `drop-shadow` sigue la silueta ya
       recortada, chaflanes incluidos.
     · La REACCIÓN. Un fogonazo interior al pulsar, que entra en 40 ms y se
       apaga en 340. Entrar rápido y salir lento es lo que hace que un botón se
       sienta ENCENDIDO por el dedo en lugar de simplemente cambiar de color.

   Encaja con la onda de toque de `tap()` (ver `styles/components.css`): el
   recorte también la recorta, así que la onda se queda dentro de la chapa.
   ========================================================================= */
.btn{
  --chaflan:11px;
  clip-path:polygon(
    var(--chaflan) 0, calc(100% - var(--chaflan)) 0,
    100% var(--chaflan), 100% calc(100% - var(--chaflan)),
    calc(100% - var(--chaflan)) 100%, var(--chaflan) 100%,
    0 calc(100% - var(--chaflan)), 0 var(--chaflan));
  border-radius:0;
  font-style:italic;
  letter-spacing:1.1px;

  /*
   * EL TEXTO NUNCA ENTRA EN LA ESQUINA CORTADA.
   *
   * El chaflán se COME las cuatro esquinas, así que el relleno de antes —que
   * bastaba con un rectángulo— dejaba las letras justo sobre el corte: en el
   * diálogo de revivir, «REVIVIR GRATIS» partía en dos líneas y ambas tocaban
   * el borde. El relleno se ata al propio chaflán para que crezca con él y no
   * haya que recordar ajustarlo en cada tamaño.
   *
   * La derecha lleva 2 px más que la izquierda: la cursiva inclina las letras
   * hacia ese lado y el último glifo se acerca más al borde que el primero.
   * Con relleno simétrico se ve descentrado aunque los números digan que no.
   */
  padding-inline:calc(var(--chaflan) + 10px) calc(var(--chaflan) + 12px);
  /* Etiquetas de dos líneas: centradas y con aire, en vez de pegadas. */
  text-align:center;
  line-height:1.18;
  overflow-wrap:break-word;
  hyphens:none;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.42),
    inset 0 -2px 0 rgba(0,0,0,.34),
    inset 2px 0 0 rgba(255,255,255,.10),
    inset -2px 0 0 rgba(0,0,0,.18);

  /*
   * EL ESTIRÓN AL SOLTAR: DOS CAUSAS, LAS DOS AQUÍ.
   *
   * 1) LA CURVA. `--ease-back` es `cubic-bezier(.34, 1.56, .64, 1)` y ese 1,56
   *    SE PASA DEL VALOR FINAL a propósito: es una curva de rebote, pensada
   *    para que algo ENTRE en pantalla con gracia. Aplicada al soltar un botón,
   *    la escala vuelve de 0,955 a 1 pasando por ~1,02 — y eso es exactamente
   *    el estirón raro. Se nota sobre todo cuando el botón no hace nada, porque
   *    no hay ninguna pantalla nueva que tape el rebote.
   *
   * 2) EL FILTRO. En reposo era `drop-shadow(...)` y al pulsar
   *    `brightness(...) drop-shadow(...)`: DOS LISTAS DE FUNCIONES DISTINTAS.
   *    El navegador no sabe interpolar entre listas que no coinciden, así que
   *    en vez de una transición hace un salto seco y vuelve a rasterizar el
   *    elemento —con `clip-path` de por medio, se ve como un tirón de tamaño—.
   *
   * Arreglo: curva sin rebote, y las dos listas de filtro con la MISMA forma
   * (`brightness` + `drop-shadow` siempre), variando sólo los valores. El
   * resplandor de cada variante viaja en `--brillo` para no romper esa forma.
   */
  --brillo:0 2px 6px rgba(0,0,0,.5);
  filter:brightness(1) drop-shadow(var(--brillo));
  transition:transform var(--t-fast) var(--ease-out),
             filter var(--t-fast) var(--ease-out);
}
.btn.sm{ --chaflan:8px; }
.btn.xs{ --chaflan:6px; }
.btn.big{ --chaflan:15px; }

/* Cada variante cambia SÓLO el valor del resplandor, nunca la forma del
   filtro: si una lo dejara en `none`, volvería el salto al pulsar. */
.btn.action{ --brillo:0 2px 7px rgba(18,164,85,.55); }
.btn.premium{ --brillo:0 2px 7px rgba(255,176,32,.45); }
.btn.danger{ --brillo:0 2px 7px rgba(142,17,48,.5); }
/* El fantasma es secundario: si brilla, compite con el principal. Sombra casi
   nula, pero la función sigue ahí para conservar la forma del filtro. */
.btn.ghost{ --brillo:0 1px 2px rgba(0,0,0,.35); box-shadow:inset 0 1px 0 rgba(255,255,255,.10); }

/* Acabado RTX compartido del botón Iniciar asalto. */
.btn:not(.btn-asalto){color:#eefcff !important;border:1px solid rgba(124,220,255,.66);box-shadow:0 0 8px rgba(38,181,255,.38),0 0 20px rgba(0,100,190,.18),inset 0 1px 0 rgba(224,255,255,.56),inset 0 -3px 0 rgba(0,24,48,.55) !important;text-shadow:0 0 7px rgba(0,30,55,.9)}
.btn:not(.btn-asalto):not(.action):not(.premium):not(.danger):not(.ghost){background:linear-gradient(180deg,#2583b3,#123b68) !important}
.btn:not(.btn-asalto).action{background:linear-gradient(180deg,rgba(24,255,157,.92),rgba(4,103,73,.96)) !important;border-color:rgba(124,255,211,.8);box-shadow:0 0 8px rgba(18,255,145,.55),0 0 22px rgba(0,190,117,.28),inset 0 1px 0 rgba(224,255,244,.7),inset 0 -3px 0 rgba(0,45,31,.55) !important}
.btn:not(.btn-asalto)::before{content:"";position:absolute;inset:3px;pointer-events:none;border:1px solid rgba(215,255,242,.42);clip-path:inherit;opacity:.9}
.btn:not(.btn-asalto)::after{background:linear-gradient(90deg,transparent,rgba(225,255,244,.7),transparent)}
.btn:not(.btn-asalto):hover{filter:brightness(1.18) drop-shadow(0 0 10px rgba(24,255,154,.7))}
.btn:not(.btn-asalto).premium{background:linear-gradient(180deg,#ffd85a,#8e4c09) !important;box-shadow:0 0 9px rgba(255,190,38,.6),inset 0 1px 0 rgba(255,255,220,.7),inset 0 -3px 0 rgba(74,33,0,.55) !important}
.btn:not(.btn-asalto).danger{background:linear-gradient(180deg,#ff6686,#941d42) !important;box-shadow:0 0 9px rgba(255,71,111,.62),inset 0 1px 0 rgba(255,230,236,.7),inset 0 -3px 0 rgba(69,5,26,.55) !important}
.btn:not(.btn-asalto).ghost{background:linear-gradient(180deg,rgba(22,113,95,.72),rgba(5,38,45,.92)) !important;border-color:rgba(87,224,190,.62);box-shadow:0 0 7px rgba(22,220,169,.34),inset 0 1px 0 rgba(208,255,243,.35) !important}

/* La misma chapa RTX para controles que no usan la clase .btn. */
.nav-item:not(.big),.tab,.pn-btn,.dm-btn-claim,.btn-update{
  position:relative; border:1px solid rgba(91,232,191,.58) !important;
  box-shadow:0 0 8px rgba(52,190,255,.3),inset 0 1px 0 rgba(220,255,244,.34),inset 0 -2px 0 rgba(0,20,40,.55) !important;
  text-shadow:0 0 7px rgba(0,35,60,.85);
}
.nav-item:not(.big),.tab{background:transparent !important;border-color:rgba(80,190,255,.34) !important}
.nav-item:not(.big){filter:grayscale(1) opacity(.52);box-shadow:none !important}
.nav-item:not(.big){border-color:transparent !important;background:transparent !important}
.nav-item:not(.big)::after{display:none !important}
.nav-item:not(.big).active{filter:none;opacity:1;background:linear-gradient(180deg,rgba(25,111,165,.32),rgba(6,35,68,.48)) !important;box-shadow:0 0 10px rgba(49,182,255,.22),inset 0 1px 0 rgba(220,255,255,.3) !important}
.nav-item[data-nav="army"].active{background:linear-gradient(180deg,rgba(146,73,198,.34),rgba(49,18,91,.5)) !important;border-color:rgba(211,141,255,.7) !important}
.nav-item[data-nav="world"].active{background:linear-gradient(180deg,rgba(31,157,176,.34),rgba(5,63,89,.5)) !important;border-color:rgba(87,232,255,.7) !important}
.nav-item[data-nav="shop"].active{background:linear-gradient(180deg,rgba(205,136,45,.34),rgba(74,40,8,.5)) !important;border-color:rgba(255,211,89,.7) !important}

/* Las CTA grandes de otras pantallas usan .action big pero no .btn-asalto;
   comparten exactamente la chapa RTX del CTA de Base. */
.btn.action.big.wide:not(.btn-asalto){
  position:relative;overflow:hidden;isolation:isolate;border-radius:0 !important;
  clip-path:polygon(15px 0,calc(100% - 15px) 0,100% 15px,100% calc(100% - 15px),calc(100% - 15px) 100%,15px 100%,0 calc(100% - 15px),0 15px);
  background:linear-gradient(135deg,#0e7c46 0%,#12a455 45%,#39ff88 100%) !important;
  border:1px solid rgba(168,255,203,.72) !important;
  box-shadow:0 10px 34px rgba(18,164,85,.42),inset 0 1px 0 rgba(255,255,255,.35),inset 0 -3px 0 rgba(0,61,37,.42) !important;
}
.btn.action.big.wide:not(.btn-asalto)::before{content:"";position:absolute;inset:5px;border:1px solid rgba(185,255,231,.48);clip-path:inherit;pointer-events:none;z-index:-1}
.btn.action.big.wide:not(.btn-asalto)::after{content:"";position:absolute;top:0;left:-60%;width:45%;height:100%;background:linear-gradient(100deg,transparent,rgba(255,255,255,.42),transparent);transform:skewX(-18deg);animation:asaltoBrillo 4.4s ease-in-out infinite;pointer-events:none;z-index:-1}

/* Corrección de legibilidad para tabs: el marco queda detrás del contenido. */
.tab{color:#9fc1e8 !important;text-shadow:0 0 7px rgba(85,180,255,.42);z-index:0}
.tab>*{position:relative;z-index:2}
.tab.active{color:#eafff8 !important;background:linear-gradient(180deg,rgba(20,166,112,.62),rgba(4,65,58,.82)) !important;text-shadow:0 0 8px rgba(65,255,180,.8)}
.tab::before{z-index:0 !important;background:transparent !important}

/* Tarjetas competitivas: sus botones de coste reciben la misma chapa RTX. */
.ranking-card button,.ranking-list button,.leaderboard button,.rank-card button{
  position:relative;overflow:hidden;border-radius:0 !important;clip-path:polygon(9px 0,calc(100% - 9px) 0,100% 9px,100% calc(100% - 9px),calc(100% - 9px) 100%,9px 100%,0 calc(100% - 9px),0 9px);
  background:linear-gradient(180deg,#fff4b5,#e4a91e 58%,#a96608) !important;color:#17253d !important;border:1px solid rgba(255,232,126,.78) !important;box-shadow:0 0 10px rgba(255,190,42,.46),inset 0 1px 0 rgba(255,255,255,.85),inset 0 -3px 0 rgba(98,48,0,.35) !important;text-shadow:0 1px 0 rgba(255,255,255,.5)
}
.ranking-card button::after,.ranking-list button::after,.leaderboard button::after,.rank-card button::after{content:"";position:absolute;top:0;left:-60%;width:42%;height:100%;background:linear-gradient(100deg,transparent,rgba(255,255,255,.7),transparent);transform:skewX(-18deg);animation:asaltoBrillo 4.4s ease-in-out infinite;pointer-events:none}
.pn-btn,.dm-btn-claim,.btn-update{background:linear-gradient(180deg,rgba(29,145,110,.82),rgba(4,63,58,.94)) !important}
.nav-item:not(.big)::after,.tab::before,.pn-btn::after,.dm-btn-claim::after,.btn-update::after{
  content:"";position:absolute;inset:3px;border:1px solid rgba(195,255,235,.3);pointer-events:none;opacity:.8
}
.nav-item:not(.big):hover,.tab:hover,.pn-btn:hover,.dm-btn-claim:hover,.btn-update:hover{filter:brightness(1.16) drop-shadow(0 0 8px rgba(22,255,155,.55))}

/* CTA de Recompensa diaria. Marco técnico de dos niveles, retícula fina y
   barrido compuesto únicamente con transform/opacity para WebView móvil. */
.dm-btn-claim{
  isolation:isolate;
  position:relative;
  overflow:hidden;
  flex:0 0 auto;
  width:100%;
  min-height:60px;
  margin:0;
  padding:8px 24px 7px;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:2px;
  border:1px solid rgba(126,255,205,.92) !important;
  border-radius:0 !important;
  clip-path:polygon(13px 0,calc(100% - 13px) 0,100% 13px,100% calc(100% - 13px),calc(100% - 13px) 100%,13px 100%,0 calc(100% - 13px),0 13px);
  background:
    linear-gradient(90deg,rgba(90,255,201,.10) 1px,transparent 1px) 0 0/18px 100%,
    linear-gradient(0deg,rgba(66,238,255,.08) 1px,transparent 1px) 0 0/100% 10px,
    radial-gradient(ellipse at 50% -15%,rgba(118,255,215,.55),transparent 48%),
    linear-gradient(180deg,#159a68 0%,#08734f 50%,#03473c 100%) !important;
  color:#f2fff9 !important;
  text-shadow:0 2px 2px rgba(0,20,24,.9),0 0 10px rgba(116,255,213,.55) !important;
  box-shadow:0 0 12px rgba(43,255,178,.52),0 8px 24px rgba(0,16,22,.65),inset 0 1px 0 rgba(231,255,248,.72),inset 0 -4px 0 rgba(0,38,35,.54) !important;
  transform:translateZ(0);
  transition:transform .14s ease !important;
  -webkit-tap-highlight-color:transparent;
}
.dm-btn-claim::after{
  content:"";
  position:absolute;
  z-index:0;
  inset:4px;
  border:1px solid rgba(202,255,236,.42);
  clip-path:inherit;
  pointer-events:none;
  opacity:1;
  background:transparent;
}
.dm-btn-claim::before{
  content:"";
  position:absolute;
  z-index:1;
  top:2px;
  bottom:2px;
  left:0;
  width:24%;
  pointer-events:none;
  opacity:0;
  background:linear-gradient(100deg,transparent,rgba(239,255,249,.72),transparent);
  transform:translate3d(-145%,0,0) skewX(-18deg);
  animation:dmClaimSweep 4.8s ease-in-out infinite;
  will-change:transform,opacity;
}
.dm-btn-claim:active{transform:translateZ(0) scale(.985);filter:none !important}
.dm-btn-claim:hover{filter:none !important}
.dm-claim-main,.dm-claim-sub{position:relative;z-index:2;pointer-events:none}
.dm-claim-main{display:flex;align-items:center;justify-content:center;gap:8px;font-size:14px;letter-spacing:.85px;line-height:1}
.dm-claim-main svg{width:18px;height:18px;filter:drop-shadow(0 0 5px rgba(255,222,70,.72))}
.dm-claim-main b{font:900 italic 14px/1 var(--font-condensed,sans-serif);color:#fff;letter-spacing:1px}
.dm-claim-sub{font:800 8px/1.1 var(--font-condensed,sans-serif);color:#9fffd9;letter-spacing:2px}
@keyframes dmClaimSweep{
  0%,68%{opacity:0;transform:translate3d(-145%,0,0) skewX(-18deg)}
  74%{opacity:.62}
  90%{opacity:.08}
  94%,100%{opacity:0;transform:translate3d(525%,0,0) skewX(-18deg)}
}
@media (prefers-reduced-motion:reduce){.dm-btn-claim::before{animation:none!important;opacity:0!important}}

/*
 * Fogonazo de pulsación. Radial desde el centro, sobre todo el contenido.
 * `pointer-events:none` es obligatorio: sin él, esta capa se traga el clic del
 * botón que está decorando.
 */
/*
 * EL BOTÓN DE ASALTO SE QUEDA FUERA. Ya usa su propio `::before` para el
 * reflejo que lo cruza (más arriba, `asaltoBrillo`), y como los dos selectores
 * tienen la misma especificidad, éste —que va después— se lo habría comido sin
 * avisar: el botón principal del juego habría perdido su brillo.
 */
.btn:not(.btn-asalto)::before{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:radial-gradient(120% 160% at 50% 50%, rgba(255,255,255,.55), rgba(255,255,255,.12) 45%, transparent 72%);
  opacity:0; transition:opacity .34s var(--ease-out);
}
.btn:not(.btn-asalto):active::before{ opacity:1; transition:opacity .04s linear; }
.btn:active{ transform:scale(.955); filter:brightness(1.32) drop-shadow(var(--brillo)); }

/*
 * El texto por encima del fogonazo y del brillo.
 *
 * `:not(.tap-onda)` NO ES DEFENSA PREVENTIVA: sin él, esta regla le ganaba a
 * `.tap-onda{position:absolute}` —misma especificidad, y `holo.css` carga
 * después— y convertía la onda de toque en un elemento EN FLUJO. Dentro de un
 * botón de 41 px de alto, un hijo de flex de 431 px deforma la caja durante los
 * 460 ms que dura la animación: es el «se estira el botón» reportado, y pasaba
 * en todos los botones del juego.
 */
.btn > *:not(.tap-onda){ position:relative; z-index:2; }

/*
 * Cuando NO se puede pagar, el botón lo dice con la forma además del color.
 * Sólo con gris, un jugador con la pantalla al sol no distingue si puede
 * permitírselo.
 */
.btn[disabled]{ filter:grayscale(.85) brightness(.55); box-shadow:inset 0 0 0 1px rgba(255,255,255,.08); }

/*
 * NADA QUE SE PULSA REBOTA AL SOLTARSE.
 *
 * `--ease-back` estaba en la transición de `transform` de todos los pulsables
 * del juego —tarjetas, parcelas de edificio, filas de lista, botones de icono,
 * avatares—, no sólo de los botones. Es una curva de ENTRADA: rebota pasándose
 * del valor final, que es precioso cuando algo aparece y es un defecto cuando
 * algo vuelve de estar pulsado. Reportado como «se estira de forma rara», y se
 * nota más justo cuando la pulsación no hace nada, porque no hay nada nuevo en
 * pantalla que distraiga del rebote.
 *
 * Sólo se cambia la CURVA de esos elementos concretos; `--ease-back` se queda
 * intacto para lo que sí entra en pantalla (celebraciones, aparición de
 * paneles, la onda de toque), que es para lo que se eligió.
 */
.icon-btn,
.card,
.bslot,
.stage-row,
.list-row,
.quest-row,
.modo-card,
.avatar-btn{
  transition-timing-function:var(--ease-out) !important;
}

/*
 * EL BOTÓN DE ASALTO CONSERVA SU LATIDO.
 *
 * Su resplandor lo animaba `box-shadow`, y `clip-path` RECORTA las sombras
 * exteriores: al chaflanarlo, el halo pulsante del botón principal del juego
 * habría desaparecido sin que nada fallara. Se pasa a `drop-shadow`, que sigue
 * la silueta recortada y late igual.
 */
.btn-asalto{
  --chaflan:16px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.30) !important;
  /*
   * El halo se queda FIJO. Sigue a la silueta chaflanada (`clip-path` recorta
   * las sombras exteriores, por eso es `drop-shadow` y no `box-shadow`), pero
   * ya no se anima: el pulso lo pone `::after` con opacidad.
   */
  filter:drop-shadow(0 9px 26px rgba(18,164,85,.55));
}
.btn-asalto:active{ transform:scale(.975); filter:brightness(1.22) drop-shadow(0 0 14px rgba(57,255,136,.9)); }

/*
 * TRES BOTONES EN UNA FILA NO CABEN, Y SE APILAN.
 *
 * Medido en el diálogo de «Ejército aniquilado», que es el peor caso del juego
 * —Rendirse / Revivir gratis / 40 núcleos— sobre un móvil de 375 px: a
 * «Revivir gratis» le sobresalía el texto 2 px por cada lado, y a «Rendirse» le
 * quedaban 3 px hasta el borde. El chaflán agrava lo que ya estaba justo,
 * porque se come las esquinas donde antes cabían las letras.
 *
 * No se arregla con más relleno: el relleno no puede encoger por debajo del
 * contenido, así que la etiqueta acaba desbordando igual. Lo que sobra es la
 * fila: tres acciones lado a lado en un teléfono son tres dianas estrechas, y
 * además ésta es una decisión importante —se está ofreciendo revivir— donde el
 * jugador tiene que poder leer las tres opciones sin esfuerzo.
 *
 * Con dos botones (Cancelar / Confirmar) la fila se mantiene: ahí sí caben, y
 * apilarlos sólo alargaría el diálogo.
 */
/*
 * `flex-wrap` + `min-width` es la red de seguridad: `:has()` no existe en los
 * WebView de Android anteriores a finales de 2022, y ahí la regla de abajo se
 * ignora entera. Sin esto, en esos aparatos volvería el desbordamiento; con
 * esto, lo peor que pasa es que los botones caen a dos líneas.
 */
.popup-foot{ flex-wrap:wrap; }
.popup-foot > .btn{ flex:1 1 auto; min-width:42%; }

.popup-foot:has(> .btn:nth-child(3)){ flex-direction:column-reverse; }
.popup-foot:has(> .btn:nth-child(3)) > .btn{ width:100%; min-width:0; }
/*
 * `column-reverse` y no `column`: el orden de los botones en el código va de
 * menos a más comprometido (rendirse → revivir con anuncio → pagar), y al
 * apilar interesa que la opción principal quede ARRIBA, donde cae el pulgar y
 * donde mira la vista al abrirse el diálogo.
 */

/* =========================================================================
   8c. PESTAÑAS: LA MISMA CHAPA, EN PEQUEÑO
   =========================================================================
   Eran cápsulas redondas junto a botones angulares: dos lenguajes distintos en
   la misma pantalla. Comparten ahora el chaflán y la reacción al toque, así que
   se leen como piezas del mismo panel.

   La pestaña ACTIVA no sólo cambia de color: se ilumina por fuera y le crece
   una barra debajo. Con la pantalla al sol, el color solo no basta para saber
   dónde estás.
   ========================================================================= */
.tab{
  position:relative; overflow:hidden;
  border-radius:0;
  clip-path:polygon(7px 0, calc(100% - 7px) 0, 100% 7px, 100% calc(100% - 7px),
                    calc(100% - 7px) 100%, 7px 100%, 0 calc(100% - 7px), 0 7px);
  font-style:italic;
  /* Mismo criterio que en los botones: el corte de esquina no puede pisar la
     etiqueta, y la cursiva pide un par de píxeles más por la derecha. */
  padding-inline:15px 17px;
  white-space:nowrap;
  transition:color var(--t-fast), background var(--t-fast), filter var(--t-fast);
}
/* Mismo criterio que en los botones: la forma del filtro no cambia entre
   estados, sólo sus valores, o al pulsar salta en vez de transicionar. */
.tab{ --brillo:0 0 0 rgba(0,0,0,0); filter:brightness(1) drop-shadow(var(--brillo)); }
.tab.active{
  --brillo:0 0 8px rgba(57,255,136,.5);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.42), inset 0 -2px 0 rgba(0,0,0,.28);
}
/* Barra inferior de la activa: la posición se lee sin depender del color. */
.tab.active::after{
  content:""; position:absolute; left:18%; right:18%; bottom:0; height:2px;
  background:#04210F; opacity:.55;
}
/* Fogonazo al pulsar, igual que en los botones. */
.tab::before{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(120% 160% at 50% 50%, rgba(255,255,255,.5), transparent 70%);
  opacity:0; transition:opacity .3s var(--ease-out);
}
.tab:active::before{ opacity:1; transition:opacity .04s linear; }
.tab:active{ filter:brightness(1.28) drop-shadow(var(--brillo)); }

@media (prefers-reduced-motion: reduce){
  .btn:not(.btn-asalto)::before,
  .tab::before{ transition:none; }
  .btn-asalto{ animation:none; }
}

/* =========================================================================
   9. LA BARRA SUPERIOR RESPIRA
   Había cuatro cápsulas idénticas peleando por el mismo ancho, así que ninguna
   destacaba y todas parecían igual de importantes. Ahora sólo la moneda dura
   lleva marco: las blandas se leen como datos, que es lo que son.
   ========================================================================= */
.res:not(.hard){
  background:transparent !important;
  border-color:transparent !important;
  padding-left:2px !important; padding-right:2px !important;
}
.res:not(.hard) .ico{
  background:transparent !important;
}
.res.hard{
  border-color:rgba(85,230,255,.34) !important;
  box-shadow:0 0 14px rgba(85,230,255,.16);
}

/* =========================================================================
   ACCESIBILIDAD
   Todo lo de arriba es movimiento decorativo, así que se apaga entero. Lo que
   NO se apaga es el color ni la jerarquía: eso es información.
   ========================================================================= */
@media (prefers-reduced-motion: reduce){
  .isla-atmos .at-scan,
  .isla-atmos .at-ring,
  .isla-atmos .at-mote,
  .isla-atmos .at-fog,
  .mp-camino-flujo,
  .bslot.es-nucleo .bimg::after,
  .bstate, .bstate i,
  .btn-asalto, .btn-asalto::before,
  .nav-item.big .nico,
  .screen::after,
  .tap-onda,
  .res.flash .ico,
  .onda-obra,
  .isla.destello,
  .bslot.cae .blvl,
  .holo-line{
    animation:none !important;
  }
  /* La onda no se anima, así que no debe quedarse pintada en pantalla. */
  .onda-obra{ display:none; }
  .bstate{ opacity:1; }
  .isla-atmos .at-scan{ display:none; }
  .isla-atmos .at-ring{ opacity:.25; }
  .lanzando .base-view,
  .lanzando .hero-cta,
  .lanzando .isla-nav,
  .lanzando .holo-feed,
  .launch-flash{ animation:none !important; }
  .launch-flash{ display:none; }
}

/* =========================================================================
   8d. ATAJOS FLOTANTES DE LA BASE — LA MISMA CHAPA QUE TODO LO DEMÁS
   =========================================================================
   Ruleta, Lab ADN y Bestias eran cápsulas redondeadas con borde fino, mientras
   los botones y las pestañas del juego ya son chapa biselada con chaflán. Tres
   pastillas flotando sobre una base militar: se leían como pegatinas de otra
   aplicación.

   Comparten ahora el mismo lenguaje —chaflán, cursiva, relieve interior,
   fogonazo al pulsar— y cada uno conserva SU color en `--mc`, que es lo que
   permite distinguirlos de un vistazo sin leerlos.
   ========================================================================= */
.btn-base-roulette,
.btn-base-dna,
.btn-base-beasts{
  --chaflan:9px;
  --mc:#FFD34E;
  border-radius:0 !important;
  border:0 !important;
  clip-path:polygon(
    var(--chaflan) 0, 100% 0, 100% calc(100% - var(--chaflan)),
    calc(100% - var(--chaflan)) 100%, 0 100%, 0 var(--chaflan));
  padding:7px 13px 7px 11px !important;
  font-style:italic;
  font-weight:900;
  letter-spacing:.9px;
  background:linear-gradient(150deg,
    color-mix(in srgb, var(--mc) 26%, rgba(10,14,28,.94)),
    rgba(8,11,22,.95)) !important;
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--mc) 55%, transparent),
    inset 0 -2px 0 rgba(0,0,0,.45),
    inset 2px 0 0 color-mix(in srgb, var(--mc) 30%, transparent);
  /* El resplandor con `drop-shadow` y no `box-shadow`: el chaflán recorta las
     sombras exteriores y un `box-shadow` desaparecería (ya pasó con el botón
     de asalto). */
  filter:brightness(1) drop-shadow(0 2px 8px color-mix(in srgb, var(--mc) 40%, transparent));
  transition:transform var(--t-fast) var(--ease-out), filter var(--t-fast) var(--ease-out);
}
.btn-base-dna{ --mc:#00F0FF; }
.btn-base-beasts{ --mc:#A855F7; }

/*
 * SIN FILO EN EL BORDE DERECHO.
 *
 * Había un `::after` de 3 px con resplandor pegado al canto derecho de los tres
 * atajos (Ruleta, ADN Lab, Bestias). La intención era que se leyeran como
 * «pestañas acopladas» al lateral, pero en pantalla, a ese tamaño, se lee como
 * un carácter suelto pegado al texto —«RULETA |»— y ensucia el botón. El chaflán
 * y el color del borde ya los anclan al lateral de sobra.
 */

/* Fogonazo al pulsar, idéntico al de los botones normales. */
.btn-base-roulette::before,
.btn-base-dna::before,
.btn-base-beasts::before{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(120% 160% at 50% 50%, rgba(255,255,255,.5), transparent 70%);
  opacity:0; transition:opacity .32s var(--ease-out);
}
.btn-base-roulette:active::before,
.btn-base-dna:active::before,
.btn-base-beasts:active::before{ opacity:1; transition:opacity .04s linear; }
.btn-base-roulette:active,
.btn-base-dna:active,
.btn-base-beasts:active{
  transform:scale(.955);
  filter:brightness(1.35) drop-shadow(0 2px 8px color-mix(in srgb, var(--mc) 40%, transparent));
}

/* El icono hereda el color del atajo; el texto se queda blanco para que se lea. */
.btn-base-roulette .ic,
.btn-base-dna .ic,
.btn-base-beasts .ic{ color:var(--mc); }

/*
 * LA INSIGNIA LATE SÓLO SI HAY ALGO QUE RECOGER.
 * Un punto rojo permanente deja de significar nada en dos días; uno que sólo
 * aparece cuando hay giro gratis o mutación mejorable sigue funcionando.
 */
/*
 * La Ruleta queda fuera: su insignia va CENTRADA encima del botón con un
 * `translateX(-50%)`, y `atajoInsignia` escribe `transform` sin arrastrarlo,
 * así que le anulaba el centrado y la tiraba a la derecha. Tiene su propio
 * pulso en `roulette.css`, que sí lo respeta.
 */
.btn-base-dna .dna-badge,
.btn-base-beasts .beast-badge{
  animation:atajoInsignia 1.6s ease-in-out infinite;
}
/*
 * PARTIDA EN DOS: el escalado se queda aquí (`transform` es barato, lo compone
 * la GPU) y el anillo que se expande pasa a un pseudo-elemento que sólo anima
 * `transform` y `opacity`. Antes el anillo era un `box-shadow` animado, y eso
 * repinta la insignia en cada fotograma mientras esté a la vista.
 *
 * El anillo se dibuja con una sombra ESTÁTICA de 5 px y se agranda escalando,
 * que es lo mismo que veía el ojo: un aro que sale de la insignia y se apaga.
 */
@keyframes atajoInsignia{
  0%,100%{ transform:scale(1); }
  50%    { transform:scale(1.12); }
}
.btn-base-dna .dna-badge::after,
.btn-base-beasts .beast-badge::after{
  content:""; position:absolute; inset:0;
  border-radius:inherit; pointer-events:none;
  box-shadow:0 0 0 5px rgba(255,77,109,.55);
  animation:atajoAnillo 1.6s ease-in-out infinite;
}
@keyframes atajoAnillo{
  0%     { opacity:1; transform:scale(.86); }
  50%,100%{ opacity:0; transform:scale(1.15); }
}

@media (prefers-reduced-motion: reduce){
  .btn-base-roulette::before, .btn-base-dna::before, .btn-base-beasts::before{ transition:none; }
  .btn-base-roulette .rl-badge-free,
  .btn-base-dna .dna-badge,
  .btn-base-beasts .beast-badge{ animation:none; }
}

/* =========================================================================
   9. LA OBRA SE VE MIENTRAS DURA
   ------------------------------------------------------------------------
   Un edificio en construcción era el edificio de siempre con un reloj encima.
   Tres horas mirando un número que baja no producen ninguna satisfacción al
   terminar, porque no hubo nada que ver avanzar: la recompensa necesita una
   rampa, y la rampa es esto.

   Tres capas, ninguna con coste de disposición: el andamio holográfico, la
   barra de avance real y las chispas. Todo se anima con `transform` y
   `opacity`, que el compositor mueve sin repintar — importa porque puede haber
   varias obras a la vez sobre un móvil modesto.
   ========================================================================= */
.bslot.obrando .bimg{
  /* Traslúcido y frío: todavía no es un edificio, es un proyecto. */
  filter:saturate(.55) brightness(.82);
}
.bslot.obrando .bimg::before{
  /* Andamio: rejilla holográfica que envuelve el volumen. */
  content:''; position:absolute; inset:6% 4% 10%; pointer-events:none;
  background:
    repeating-linear-gradient(90deg, rgba(90,200,245,.30) 0 1px, transparent 1px 11px),
    repeating-linear-gradient( 0deg, rgba(90,200,245,.22) 0 1px, transparent 1px 13px);
  mask-image:linear-gradient(180deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
  /*
   * Éste se queda con `background-position` A PROPÓSITO.
   *
   * Se probó a moverlo con `transform`, que es lo correcto en general, pero
   * aquí la máscara de difuminado vive en este mismo elemento: al desplazar la
   * caja se desplazaba también la máscara, y los bordes difuminados del
   * andamio se movían 13 px con ella. El repintado que ahorraba no compensaba
   * romper el efecto —es un elemento pequeño, dentro del icono de un edificio,
   * y sólo existe mientras hay una obra en curso—.
   */
  animation:obraAndamio 2.4s linear infinite;
}
@keyframes obraAndamio{
  0%  { opacity:.32; background-position:0 0, 0 0; }
  50% { opacity:.72; }
  100%{ opacity:.32; background-position:0 -13px, 0 0; }
}
/*
 * Chispa de soldadura. Un solo punto que recorre el andamio: con más de uno
 * deja de leerse como una soldadura y pasa a parecer estática.
 */
.bslot.obrando .bimg::after{
  content:''; position:absolute; left:50%; top:34%; width:5px; height:5px;
  border-radius:50%; background:#FFE9A8; pointer-events:none;
  box-shadow:0 0 8px 3px rgba(255,196,60,.75);
  animation:obraChispa 1.9s var(--ease-out) infinite;
}
@keyframes obraChispa{
  0%   { transform:translate(-140%, 40%) scale(.5); opacity:0; }
  18%  { opacity:1; }
  55%  { transform:translate( 90%, -30%) scale(1); opacity:1; }
  70%  { opacity:.9; }
  100% { transform:translate(150%, 60%) scale(.4); opacity:0; }
}

/* Barra de avance, apoyada en la losa y no flotando sobre el tejado: es el
   suelo de la obra lo que se va completando. */
.bobra{
  position:absolute; left:50%; top:79%; transform:translateX(-50%);
  width:56px; height:5px; border-radius:99px; overflow:hidden;
  background:rgba(4,7,16,.85); border:1px solid rgba(90,200,245,.35);
  box-shadow:0 1px 5px rgba(0,0,0,.6);
}
.bobra i{
  position:absolute; inset:0;
  background:repeating-linear-gradient(115deg,
    rgba(255,197,61,.16) 0 5px, transparent 5px 10px);
}
.bobra b{
  position:absolute; left:0; top:0; bottom:0; width:2%;
  background:linear-gradient(90deg,#FFB92E,#FFE9A8);
  box-shadow:0 0 7px rgba(255,185,46,.85);
  /* Un segundo exacto: la barra se mueve al mismo ritmo que el tic que la
     alimenta, así que avanza continua en vez de a saltos. */
  transition:width 1s linear;
}

/* =========================================================================
   10. EL REMATE: CUANDO LA OBRA TERMINA
   ------------------------------------------------------------------------
   El aterrizaje del edificio ya existía. Lo que faltaba era el instante justo
   anterior: la columna de luz que sube desde la losa y el anillo que sale
   disparado por el suelo. Duran menos de un segundo entre las dos y son lo que
   convierte «apareció el edificio» en «acaba de levantarse ahí».
   ========================================================================= */
.bslot.cae .bimg::before,
.bslot.cae .bimg::after{ content:none; }   /* el andamio ya no pinta nada */

.bslot.cae .columna-obra{
  position:absolute; left:50%; top:71.9%; transform:translate(-50%,-100%);
  width:34px; height:150px; pointer-events:none; z-index:-1;
  background:linear-gradient(180deg, transparent, rgba(90,200,245,.55) 55%, rgba(190,240,255,.9));
  mask-image:linear-gradient(180deg, transparent, #000 40%, #000 88%, transparent);
  animation:columnaObra .82s var(--ease-out) forwards;
}
@keyframes columnaObra{
  0%  { opacity:0; transform:translate(-50%,-100%) scaleY(.2) scaleX(1.6); }
  22% { opacity:1; }
  100%{ opacity:0; transform:translate(-50%,-100%) scaleY(1.15) scaleX(.35); }
}

@media (prefers-reduced-motion: reduce){
  .bslot.obrando .bimg::before,
  .bslot.obrando .bimg::after,
  .bslot.cae .columna-obra{ animation:none; }
  .bobra b{ transition:none; }
}

/* =========================================================================
   11. LOS PANELES ENTRAN EN CASCADA
   ------------------------------------------------------------------------
   La pantalla entera se desvanecía como un bloque. Correcto, y plano: todo
   aparece a la vez, así que no hay nada que MIRAR llegar. Escalonando los
   paneles cincuenta milisegundos entre uno y otro, el cambio de menú pasa a
   leerse como una interfaz que se monta delante de ti — el mismo truco que
   usan las consolas y los juegos caros, y cuesta doce líneas de CSS.

   Sólo los seis primeros llevan retardo. Más allá el escalonado deja de
   percibirse como cascada y empieza a percibirse como lentitud, que es
   exactamente lo contrario de lo que se busca; el resto entra con el último.

   `backwards` es imprescindible: sin él, el panel se ve a opacidad plena
   durante su retardo y luego SALTA al inicio de la animación.
   ========================================================================= */
/*
   SOLO CON `.entra` (la pone applyStagger al cambiar de pantalla, y NUNCA la
   pone refresh()). Sin esa condicion, esta regla se aplicaba a cualquier hijo
   de .screen-body en todo momento: cada refresco —una compra, un orbe, el
   tutorial avanzando— reconstruia el DOM y TODA la pantalla volvia a nacer con
   su animacion de entrada. Ese era el parpadeo constante del menu.
*/
.screen.entra:not(.out) .screen-body > *{
  animation: none !important;
}
.screen.entra:not(.out) .screen-header{
  animation: none !important;
}

@media (prefers-reduced-motion: reduce){
  .screen.entra:not(.out) .screen-body > *,
  .screen.entra:not(.out) .screen-header{ animation:none; }
}

/* =========================================================================
   12. LA PILA DE ATAJOS VA ARRIBA A LA IZQUIERDA
   ------------------------------------------------------------------------
   Estaban a la derecha y centrados en vertical, y ahí molestaban dos veces: se
   comían el borde derecho de la isla y quedaban justo encima del aviso de
   «MEJORABLE» del Centro de Mando. El jugador pidió moverlos al hueco de
   arriba a la izquierda, que es tierra de nadie —queda por encima de la costa
   y por debajo del rótulo de la isla— y donde no tapan nada.

   LA POSICIÓN SE FIJA AQUÍ Y EN UN SOLO SITIO. Antes vivía repartida entre
   `roulette.css`, `dna.css` y `beasts.css`, con tres `top: calc(50% ± 21px)`
   que había que mantener coherentes a mano: mover la pila obligaba a acertar
   tres números en tres ficheros, y bastaba equivocarse en uno para que un botón
   se quedara descolgado. Ahora es una rejilla de una columna.

   Los tres botones son hermanos sueltos dentro de `.base-view` (no hay un nodo
   que los agrupe), así que la pila se arma dando a cada uno su fila explícita.
   Es lo que permite reordenarlos sin tocar el JS.
   ========================================================================= */
.base-view{
  display:grid;
  /* Una sola celda: la isla ocupa la vista entera y los atajos se colocan
     encima con posicionamiento absoluto, como hasta ahora. */
  grid-template-columns:minmax(0,1fr);
}
.btn-base-roulette,
.btn-base-dna,
.btn-base-beasts{
  left:10px;
  right:auto;
  /*
   * Anclados ARRIBA y no al centro. Al centro, la pila se movía con la altura
   * del móvil y en las pantallas cortas acababa sobre los edificios; medida
   * desde el techo, cae siempre en el mismo hueco.
   *
   * 78 px es justo debajo de los puntos de navegación entre islas
   * (`.isla-nav` está a 40 px y mide unos 26).
   */
  top:78px;
  z-index:12;
  /* Ancho común: tres píldoras de anchos distintos apiladas se leen como un
     error de maquetación, no como un menú. */
  min-width:104px;
  justify-content:flex-start;
}
.btn-base-dna   { top:118px; }
.btn-base-beasts{ top:158px; }

/*
 * La insignia se pasa al lado DERECHO. Con la pila a la izquierda, un punto
 * rojo colgando por la esquina izquierda se sale del área visible del móvil.
 *
 * LA RULETA QUEDA FUERA: su «¡GRATIS!» ya no cuelga de una esquina, va
 * centrado ENCIMA del botón (ver `roulette.css`). Esta regla llegaba después
 * en la cascada y su `left:auto` le habría anulado el centrado, dejándolo otra
 * vez pegado al borde derecho. Las otras dos siguen siendo puntos de esquina y
 * sí lo necesitan.
 */
.btn-base-dna .dna-badge,
.btn-base-beasts .beast-badge{
  left:auto; right:-6px;
}

/*
 * En pantallas cortas la pila se aprieta para no invadir la isla. Por debajo de
 * 700 px de alto el hueco entre el rótulo y la costa se reduce, y tres botones
 * de 40 px de paso ya no caben sin pisar el terreno.
 */
@media (max-height: 700px){
  .btn-base-roulette{ top:66px; }
  .btn-base-dna     { top:102px; }
  .btn-base-beasts  { top:138px; }
}

/* =========================================================================
   ROBOTS Y TRABAJADORES CON PROPULSIÓN DE FUEGO EN LA BASE
   ========================================================================= */
.isla-workers-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 85;
  overflow: visible;
  transform: translateZ(0);
  will-change: transform;
  contain: layout paint;
  isolation: isolate;
}

.worker-track {
  position: absolute;
  width: 48px;
  height: 52px;
  margin-left: -24px;
  margin-top: -26px;
  pointer-events: auto !important;
  cursor: pointer;
  will-change: transform, left, top, opacity;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: filter 0.2s ease, transform 0.2s ease;
  user-select: none;
  touch-action: manipulation;
  transform: translateZ(0);
  backface-visibility: hidden;
}

.worker-track:hover, .worker-track:active {
  filter: drop-shadow(0 0 12px #00F0FF) brightness(1.25);
}

/* Sombra en el suelo */
.wb-ground-shadow, .wb-drone-shadow {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 28px;
  height: 9px;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.7) 0%, transparent 75%);
  transform: translate(-50%, 0);
  border-radius: 50%;
}

/* Llamas de fuego / plasma en los pies */
.wb-jet-flames {
  transform-origin: center top;
  animation: jetThrust 0.2s infinite alternate ease-in-out;
  filter: drop-shadow(0 0 8px #00f0ff);
  will-change: transform, opacity;
  transform: translateZ(0);
}

@keyframes jetThrust {
  0% { transform: scaleY(0.85) scaleX(0.95); opacity: 0.8; }
  100% { transform: scaleY(1.35) scaleX(1.1); opacity: 1; }
}

.wb-visor {
  animation: visorPulse 1.8s infinite alternate;
  filter: drop-shadow(0 0 5px #00f0ff);
  will-change: opacity;
  transform: translateZ(0);
}
@keyframes visorPulse {
  0% { opacity: 0.55; }
  100% { opacity: 1; }
}

.wb-beacon {
  animation: beaconBlink 0.9s infinite alternate;
}
@keyframes beaconBlink {
  0% { opacity: 0.3; }
  100% { opacity: 1; }
}

/* RUTA 1: Vuelo / Patrulla Primaria */
.worker-track-1 {
  left: var(--track-x1, 30%);
  top: var(--track-y1, 40%);
  animation: workerRoute1 18s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

@keyframes workerRoute1 {
  0% {
    left: var(--track-x1, 30%);
    top: calc(var(--track-y1, 40%) - 10px);
    transform: scale(0.95);
  }
  20%, 45% {
    left: var(--track-x1, 30%);
    top: var(--track-y1, 40%);
    transform: scale(1.02);
  }
  60% {
    left: calc((var(--track-x1, 30%) + var(--track-x2, 70%)) / 2);
    top: calc((var(--track-y1, 40%) + var(--track-y2, 60%)) / 2 - 15px);
    transform: scale(1.08);
  }
  75%, 90% {
    left: var(--track-x2, 70%);
    top: var(--track-y2, 60%);
    transform: scale(0.98);
  }
  100% {
    left: var(--track-x1, 30%);
    top: calc(var(--track-y1, 40%) - 10px);
    transform: scale(0.95);
  }
}

/* RUTA 2: Patrulla Terrestre / Mecánico */
.worker-track-2 {
  left: var(--track-x2, 65%);
  top: var(--track-y2, 55%);
  animation: workerRoute2 15s ease-in-out infinite;
}

@keyframes workerRoute2 {
  0%, 100% {
    left: var(--track-x2, 65%);
    top: var(--track-y2, 55%);
    transform: scaleX(1);
  }
  42%, 48% {
    left: var(--track-x1, 35%);
    top: var(--track-y1, 45%);
    transform: scaleX(1);
  }
  50%, 92% {
    left: var(--track-x1, 35%);
    top: var(--track-y1, 45%);
    transform: scaleX(-1);
  }
  96% {
    left: var(--track-x2, 65%);
    top: var(--track-y2, 55%);
    transform: scaleX(-1);
  }
}

/* RUTA 3: Órbita Aérea / Suministro */
.worker-track-3 {
  left: var(--track-x1, 50%);
  top: var(--track-y1, 30%);
  animation: workerRoute3 14s ease-in-out infinite alternate;
}

@keyframes workerRoute3 {
  0% {
    left: var(--track-x1, 40%);
    top: var(--track-y1, 30%);
    transform: translateY(0px) rotate(-3deg);
  }
  50% {
    left: calc((var(--track-x1, 40%) + var(--track-x2, 60%)) / 2);
    top: calc((var(--track-y1, 30%) + var(--track-y2, 50%)) / 2 - 12px);
    transform: translateY(-8px) rotate(3deg);
  }
  100% {
    left: var(--track-x2, 60%);
    top: var(--track-y2, 50%);
    transform: translateY(0px) rotate(-3deg);
  }
}

/* EFECTOS ESPECÍFICOS DE ROBOTS */

/* Cono de escaneo láser */
.wb-scan-cone {
  animation: scanConePulse 4s ease-in-out infinite;
  filter: drop-shadow(0 0 8px #00F0FF);
  will-change: transform, opacity;
  transform: translateZ(0);
}
@keyframes scanConePulse {
  0%, 100% { opacity: 0.18; transform: scaleY(0.8); }
  50% { opacity: 0.85; transform: scaleY(1.15); }
}

/* Chispas de soldadura láser */
.wb-welder-spark {
  animation: weldSpark 0.4s ease-in-out infinite alternate;
  filter: drop-shadow(0 0 8px #38BDF8);
  will-change: transform, opacity;
  transform: translateZ(0);
}
@keyframes weldSpark {
  0% { opacity: 0.25; transform: scale(0.65); }
  100% { opacity: 1; transform: scale(1.5); }
}

/* Rotores de drones */
.drone-blade {
  animation: bladeSpin 0.1s linear infinite;
  transform-origin: center;
  will-change: transform, opacity;
  transform: translateZ(0);
}
@keyframes bladeSpin {
  0% { transform: scaleX(1); }
  50% { transform: scaleX(0.2); opacity: 0.6; }
  100% { transform: scaleX(1); }
}

/* Taladro minero rotatorio */
.wb-drill-head {
  animation: drillRotate 0.15s linear infinite;
  transform-origin: 34px 26px;
  will-change: transform;
  transform: translateZ(0);
}
@keyframes drillRotate {
  0% { transform: scaleY(1); }
  50% { transform: scaleY(-0.8); }
  100% { transform: scaleY(1); }
}
.wb-drill-spark {
  animation: drillSpark 0.3s infinite alternate;
  filter: drop-shadow(0 0 8px #F97316);
  will-change: transform, opacity;
  transform: translateZ(0);
}
@keyframes drillSpark {
  0% { opacity: 0.35; transform: scale(0.8); }
  100% { opacity: 1; transform: scale(1.4); }
}

/* Escudo deflector hexagonal */
.wb-shield-field {
  animation: shieldPulse 2s ease-in-out infinite;
  filter: drop-shadow(0 0 8px #FBBF24);
  will-change: transform, opacity;
  transform: translateZ(0);
}
@keyframes shieldPulse {
  0%, 100% { opacity: 0.35; transform: scale(0.95); }
  50% { opacity: 0.95; transform: scale(1.05); }
}

/* Anillos cuánticos orbitales */
.wb-orbit-ring-1 {
  animation: ringSpin1 3s linear infinite;
  transform-origin: 23px 23px;
  will-change: transform;
  transform: translateZ(0);
}
@keyframes ringSpin1 {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.wb-orbit-ring-2 {
  animation: ringSpin2 4s linear infinite reverse;
  transform-origin: 23px 23px;
  will-change: transform;
  transform: translateZ(0);
}
@keyframes ringSpin2 {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* Prisma giratorio */
.wb-prism-spin {
  animation: prismSpin 5s ease-in-out infinite alternate;
  transform-origin: 20px 20px;
  filter: drop-shadow(0 0 8px #34D399);
  will-change: transform;
  transform: translateZ(0);
}
@keyframes prismSpin {
  0% { transform: rotate(-10deg) scale(0.95); }
  100% { transform: rotate(10deg) scale(1.05); }
}

/* Disco del vacío de antimateria */
.wb-void-disk {
  animation: voidDiskSpin 2.5s linear infinite;
  transform-origin: 22px 38px;
  filter: drop-shadow(0 0 10px #C084FC);
  will-change: transform, opacity;
  transform: translateZ(0);
}
@keyframes voidDiskSpin {
  0% { transform: scaleX(1) scaleY(1); opacity: 0.8; }
  50% { transform: scaleX(1.2) scaleY(0.8); opacity: 1; }
  100% { transform: scaleX(1) scaleY(1); opacity: 0.8; }
}

/* Micro agujeros negros orbitando */
.wb-orbit-orb-1, .wb-orbit-orb-2, .wb-orbit-orb-3 {
  animation: orbitOrb 3s ease-in-out infinite alternate;
  filter: drop-shadow(0 0 8px #E879F9);
  will-change: transform;
  transform: translateZ(0);
}
@keyframes orbitOrb {
  0% { transform: translateY(-4px); }
  100% { transform: translateY(4px); }
}

/* Acabado de referencia para el botón de asalto y la barra inferior. */
.btn-asalto{
  min-height:70px !important;
  padding:10px 28px !important;
  border-radius:0 !important;
  clip-path:polygon(2.5% 0,97.5% 0,100% 24%,100% 76%,97.5% 100%,2.5% 100%,0 76%,0 24%) !important;
  background:linear-gradient(180deg,rgba(7,105,65,.95),rgba(0,41,31,.98) 48%,rgba(4,117,65,.95)) !important;
  border:1px solid rgba(42,255,157,.98) !important;
  box-shadow:0 0 10px rgba(0,255,132,.74),0 0 30px rgba(0,255,132,.42),inset 0 0 20px rgba(0,255,132,.38),inset 0 1px 0 rgba(188,255,220,.9) !important;
  color:#eafff7 !important;
  filter:drop-shadow(0 0 8px rgba(0,255,157,.48)) !important;
}
.btn-asalto::before{
  inset:4px !important;
  left:4px !important;
  width:auto !important;
  height:auto !important;
  border:1px solid rgba(71,255,192,.72) !important;
  background:linear-gradient(90deg,rgba(23,255,170,.18),transparent 18%,transparent 82%,rgba(23,255,170,.18)) !important;
  clip-path:polygon(2.5% 0,97.5% 0,100% 24%,100% 76%,97.5% 100%,2.5% 100%,0 76%,0 24%) !important;
  z-index:0 !important;
  animation:none !important;
}
.btn-asalto::after{
  inset:7px !important;
  z-index:0 !important;
  opacity:.5 !important;
  background:repeating-linear-gradient(90deg,rgba(93,255,205,.11) 0 1px,transparent 1px 12px),linear-gradient(180deg,rgba(147,255,215,.22),transparent 28%,rgba(0,255,157,.22)) !important;
  animation:asaltoReflejo 4.8s ease-in-out infinite !important;
}
.btn-asalto .as-txt{position:relative;z-index:2;gap:0;line-height:1.05}
.btn-asalto .as-main{font-family:var(--f-ui);font-size:22px !important;font-style:italic;font-weight:950;letter-spacing:.14em;background:linear-gradient(180deg,#eafff6 0%,#a5ffd2 28%,#35ff9a 62%,#b6ffe0 100%);-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:0 0 8px rgba(0,255,132,.72),0 2px 4px rgba(0,0,0,.75)}
.btn-asalto .as-main .ic,.btn-asalto .as-main svg{color:#b9ffe0;filter:drop-shadow(0 0 6px #18ff91)}
.btn-asalto .as-sub{font-size:0 !important;font-style:italic;font-weight:800;letter-spacing:.22em;color:#89ffc5;opacity:1}
.btn-asalto .as-sub::after{content:'EXPLORA. CONQUISTA. EVOLUCIONA.';font-size:10px;letter-spacing:.22em;background:linear-gradient(90deg,#52ffac,#d2ffeb,#52ffac);-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:0 0 7px rgba(0,255,132,.72)}
@keyframes asaltoReflejo{0%,100%{opacity:.28;transform:translateX(-4%)}50%{opacity:.7;transform:translateX(4%)}}
.navbar{background:linear-gradient(180deg,#031c2a 0%,#02121e 100%) !important;border-top:1px solid rgba(85,206,255,.4) !important;box-shadow:0 -5px 24px rgba(0,128,210,.22),inset 0 1px 0 rgba(117,232,255,.16) !important}
.nav-item{color:#76b8e1 !important;font-family:var(--f-ui);font-size:11px !important;font-style:italic;font-weight:800;letter-spacing:.14em;text-shadow:0 0 8px rgba(73,194,255,.3)}
.nav-item .nico{font-size:27px !important;color:#6bd2ff !important;filter:drop-shadow(0 0 6px rgba(54,194,255,.7)) !important}
.nav-item.active{color:#f0fcff !important}.nav-item.active .nico{filter:drop-shadow(0 0 12px #43cfff) !important}

/* ==========================================================================
   RECOMPENSAS DIARIAS — TARJETAS GRÁFICAS & ANIMACIONES CINEMÁTICAS
   ========================================================================== */
.dm-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  overflow-y: auto;
  padding: 4px;
  margin-bottom: 14px;
  flex: 1;
}

@media (max-width: 480px) {
  .dm-grid {
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
  }
}

.dm-card-item {
  position: relative;
  width: 100%;
  aspect-ratio: 0.82;
  border-radius: 12px;
  overflow: hidden;
  border: 1.5px solid rgba(255, 255, 255, 0.12);
  background: #07111e;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 5px;
  transform: translateZ(0);
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), border-color 0.2s ease, filter 0.3s ease;
  user-select: none;
  -webkit-user-select: none;
}

.dm-card-item.dm-big {
  grid-column: span 2;
  aspect-ratio: 1.72;
}

.dm-card-bg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
  pointer-events: none;
  transition: filter 0.4s ease, transform 0.4s ease;
}

.dm-card-gradient {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(180deg, rgba(3, 10, 24, 0.4) 0%, rgba(3, 10, 24, 0.05) 40%, rgba(3, 8, 20, 0.92) 100%);
  pointer-events: none;
}

.dm-card-top-row {
  position: relative;
  z-index: 3;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  width: 100%;
}

.dm-card-day {
  background: rgba(0, 0, 0, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #e2e8f0;
  font-size: 8.5px;
  font-weight: 900;
  letter-spacing: 0.5px;
  padding: 1.5px 5px;
  border-radius: 5px;
  text-shadow: 0 1px 2px #000;
}

.dm-card-today-badge {
  background: #39FF88;
  color: #000;
  font-size: 8px;
  font-weight: 950;
  letter-spacing: 0.5px;
  padding: 1.5px 6px;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(57, 255, 136, 0.8);
  animation: todayGlow 1.8s ease-in-out infinite alternate;
}

@keyframes todayGlow {
  0% { transform: scale(1); filter: drop-shadow(0 0 2px #39FF88); }
  100% { transform: scale(1.05); filter: drop-shadow(0 0 8px #39FF88); }
}

.dm-card-bottom-val {
  position: relative;
  z-index: 3;
  background: rgba(4, 12, 28, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  padding: 3px 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: 10.5px;
  font-weight: 800;
  color: #fff;
  text-shadow: 0 1px 2px #000;
}

/* Estado: Hoy sin reclamar (activo y pulsante) */
.dm-card-item.dm-is-today {
  border-color: #39FF88 !important;
  box-shadow: 0 0 16px rgba(57, 255, 136, 0.45), inset 0 0 12px rgba(57, 255, 136, 0.25);
  animation: cardPulse 2.2s ease-in-out infinite alternate;
  cursor: pointer;
}
@keyframes cardPulse {
  0% { transform: scale(1); border-color: #39FF88; }
  100% { transform: scale(1.03); border-color: #86efac; box-shadow: 0 0 22px rgba(57, 255, 136, 0.7); }
}

/* Estado: Reclamado (escala de grises) */
.dm-card-item.dm-is-claimed {
  border-color: rgba(255, 255, 255, 0.08) !important;
  box-shadow: none !important;
}
.dm-card-item.dm-is-claimed .dm-card-bg-img {
  filter: grayscale(0.92) brightness(0.42);
}
.dm-card-item.dm-is-claimed .dm-card-bottom-val {
  background: rgba(15, 23, 42, 0.7);
  color: #64748b;
  border-color: rgba(255, 255, 255, 0.08);
}

/* Estado: Futuro bloqueado */
.dm-card-item.dm-is-locked .dm-card-bg-img {
  filter: brightness(0.6) saturate(0.8);
}
.dm-card-lock-overlay {
  position: absolute;
  inset: 0;
  z-index: 4;
  background: rgba(2, 6, 15, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.dm-card-lock-overlay svg {
  width: 22px;
  height: 22px;
  color: rgba(255, 255, 255, 0.4);
  filter: drop-shadow(0 2px 4px #000);
}

/* Sello Militar: RECLAMADO ✓ */
.dm-stamp-container {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.dm-stamp-badge {
  background: rgba(15, 23, 42, 0.92);
  border: 2px solid #22c55e;
  color: #4ade80;
  font-family: var(--f-ui, system-ui);
  font-style: italic;
  font-weight: 950;
  font-size: 10px;
  letter-spacing: 1.5px;
  padding: 3px 8px;
  border-radius: 4px;
  text-transform: uppercase;
  box-shadow: 0 0 14px rgba(34, 197, 94, 0.7), inset 0 0 6px rgba(34, 197, 94, 0.3);
  transform: rotate(-14deg);
  text-shadow: 0 0 6px rgba(34, 197, 94, 0.9);
  animation: stampDrop 0.38s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

@keyframes stampDrop {
  0% {
    transform: scale(2.8) rotate(-24deg);
    opacity: 0;
  }
  60% {
    transform: scale(0.92) rotate(-12deg);
    opacity: 1;
  }
  80% {
    transform: scale(1.06) rotate(-15deg);
  }
  100% {
    transform: scale(1) rotate(-14deg);
    opacity: 1;
  }
}

/* Destello de luz al reclamar */
.dm-claim-flash {
  position: absolute;
  inset: 0;
  z-index: 6;
  background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.95) 0%, rgba(57, 255, 136, 0.8) 50%, transparent 80%);
  pointer-events: none;
  animation: claimFlashAnim 0.6s ease-out forwards;
}

@keyframes claimFlashAnim {
  0% { opacity: 0; transform: scale(0.7); }
  35% { opacity: 1; transform: scale(1.2); }
  100% { opacity: 0; transform: scale(1.6); }
}
