/* =====================================================
   ENGLISH 11 — theme
   Edit these variables to change the whole look quickly.
   ===================================================== */
:root {
  --bg-1: #131b20;            /* fondo de la pagina (plano) */
  /* 🚨 NO se usa en la pagina: el fondo es PLANO desde el 2026-09-09, y este es
     el unico sitio donde queda escrito el token. Parece codigo muerto y no lo
     es: lo lee tools/build_og_image.py (read_palette) para el degradado de la
     imagen de Open Graph y del master del que salen los favicons, y ese script
     hace `raise SystemExit` si no encuentra la declaracion. Borrarlo no rompe
     el CSS — rompe el build de las imagenes, y en silencio hasta que alguien
     lo vuelva a correr.
     ⚠️ Y OJO CON EL TEMA: read_palette() lee --bg-1 con un re.search, o sea la
     PRIMERA coincidencia del archivo, que es la de aqui arriba — el teal — pase
     lo que pase. El script NO SABE que existe data-theme, asi que con la paleta
     morada puesta generaria imagenes teal sin avisar.
     Y aun con el teal puesto, como ahora, la pareja sigue descuadrada: --bg-1
     es teal y --bg-2 morado, o sea un degradado teal->morado que no se parece a
     ninguna de las dos paletas. Las imagenes que hay subidas son las moradas
     viejas. NO CORRER EL SCRIPT hasta decidir dos cosas: que paleta manda, y
     que --bg-2 le toca. */
  --bg-2: #2a0a3d;
  --panel: #1a252c;           /* superficie de panel / tarjeta */
  --panel-border: #415058;    /* el borde fuerte */
  --line-soft: #2c383f;       /* el borde suave */
  --tile: #212d34;            /* fichas, opciones, chips en reposo */
  --tile-hover: #29353d;      /* su estado levantado */
  --sunken: #0c1215;          /* inputs y celdas: el hueco, no el relieve */
  --veil: rgba(6, 10, 12, 0.8);          /* el velo de .overlay */
  --panel-glow: 0 18px 50px rgba(0, 0, 0, 0.55);  /* el relieve de los paneles flotantes */
  /* 🚨 Los cinco de abajo son --panel, --bg-1 y --panel-border CON ALFA, y
     tienen token propio por una razon concreta: `var(--panel)` no se puede
     poner transparente desde la regla que lo usa, asi que sin ellos habria
     que escribir el color a pelo — y un color a pelo DEJA DE OBEDECER a
     data-theme, que es justo lo que hace que la web se pueda repintar entera
     cambiando un atributo. Se descubrieron asi: al poner data-theme="classic"
     estas cinco piezas se quedaron teal sobre una web morada. Si anades una
     superficie translucida, hazte el token; no escribas el rgba. */
  --panel-85: rgba(26, 37, 44, 0.85);    /* .badge-counter .tagline-btn .level-option */
  --panel-97: rgba(26, 37, 44, 0.97);    /* .rd-q-panel */
  --bg-78: rgba(19, 27, 32, 0.78);       /* #rw-summary .rw-meaning */
  --entry-grad: linear-gradient(135deg, rgba(33, 46, 55, 0.9), rgba(26, 37, 44, 0.9)); /* .level-entry */
  --border-glow: rgba(65, 80, 88, 0.65); /* el resplandor de .fib-cell.up */
  --accent: #9b30ff;          /* main purple accent (buttons) */
  --accent-bright: #b95cff;
  /* Solo para resaltar la palabra de los items `synonym`: mismo tamano y
     mismo peso que el resto de la frase, solo cambia el color. Token propio
     porque --accent-bright lo usa media UI. 8,26:1 contra el panel. */
  --accent-soft: #e28bff;
  /* La bocina grande de los items de listening. Medidos del mockup de Omar:
     #8f22bc es el 82,8% de los pixeles del boton y #390a51 el color del glifo.
     Contraste entre los dos: 2,34:1 — por debajo del 3:1 recomendado para
     elementos graficos. Es su color y el glifo es grande; queda anotado. */
  --speaker-bg: #8f22bc;
  --speaker-fg: #390a51;
  --text: #f5f2fa;
  --text-dim: #c9b8d9;

  /* Wordle tile colors (classic) */
  --tile-green: #538d4e;
  --tile-yellow: #b59f3b;
  --tile-gray: #3a3a3c;
  --tile-border: #565758;
  --key-bg: #818384;
}

/* 🚨 LA PALETA CLASICA (la morada original). No es codigo muerto ni un resto:
   es el interruptor. Poniendo data-theme="classic" en el <html> de index.html
   la web entera vuelve al morado; quitandolo manda el :root de arriba y se ve
   teal. Ahora mismo NO esta puesto, o sea que se ve el teal. Probado en los dos
   sentidos el 2026-09-11, midiendo: va y vuelve sin residuos.
   Funciona porque TODAS las superficies y bordes pasan por estos catorce
   tokens. Si algun dia alguien vuelve a escribir un color a
   pelo en una regla, esa regla deja de volver — y por eso no se escriben
   colores a pelo.

   🚨 TIENE QUE IR DEBAJO del :root de arriba, y no solo por la cascada:
   read_palette() en tools/build_og_image.py busca `--bg-1:` con un re.search,
   que devuelve LA PRIMERA coincidencia del archivo. Subir este bloque le daria
   al generador de imagenes el morado viejo sin que nadie se entere.

   Lo que NO vuelve con este tema, y conviene saberlo antes de usarlo:
     - Las diez opacidades de borde que habia se colapsaron en dos niveles
       (--panel-border y --line-soft). Aqui vuelven a ser moradas, pero cada
       nivel con un solo morado, no con sus diez alfas originales.
     - El grosor de 4px de .game-card y su sombra en reposo son de otras
       tandas y no dependen del tema. Eso es `git`, no un atributo.
     - Los degradados que se colapsaron en un color plano no vuelven a ser
       degradados: .card-art era `linear-gradient(160deg,#3a1153,#1d0530)` y
       .rd-story-art `#45195e -> #1d0530`; en classic los dos salen del
       --panel morado, plano. Mismo caso en .em-emojis y #rw-summary.
     - El <meta name="theme-color"> de index.html NO es un token: se queda en
       el teal #131B20, asi que en Android la barra no acompanaria. */
:root[data-theme="classic"] {
  --bg-1: #16041f;
  --panel: #21072e;
  --panel-border: #8b2fd6;
  --line-soft: #541886;
  --tile: #2b0d3f;
  --tile-hover: #3a1153;
  --sunken: #150322;
  --veil: rgba(8, 0, 14, 0.8);
  --panel-glow: 0 0 40px rgba(155, 48, 255, 0.35);
  --panel-85: rgba(33, 7, 46, 0.85);
  --panel-97: rgba(43, 8, 61, 0.97);
  --bg-78: rgba(30, 8, 48, 0.78);
  --entry-grad: linear-gradient(135deg, rgba(42, 10, 61, 0.9), rgba(33, 7, 46, 0.9));
  /* ⚠️ Este NO es el valor que habia antes del redisenio. El original era
     rgba(168,85,247,.65), o sea --sp-accent: .fib-cell.up es un dibujo de la
     PORTADA que estaba enchufado al color de Star Party, y eso era un fallo —
     el dia que alguien cambiase el color de Star Party, cambiaban solos cuatro
     dibujos de las tarjetas. Se arreglo en la tanda del teal y aqui se
     mantiene arreglado: el morado que le toca es el de --panel-border. */
  --border-glow: rgba(139, 47, 214, 0.65);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* keeps the vertical scrollbar's reserved space symmetric on both edges, so
   centered content (grids, cards, headers) doesn't visually drift ~7px left
   whenever a screen is tall enough to scroll — no effect on layout width. */
html { scrollbar-gutter: stable both-edges; overflow-x: hidden; }

body {
  /* En iPhone, iPad y Mac esto resuelve a SF de verdad, que es la fuente de
     los mockups: 0 KB y ninguna peticion. Las fuentes de Apple no se pueden
     servir desde Netlify (su licencia es solo para plataformas Apple), asi
     que incrustarla no era opcion. En Windows la primera que existe sigue
     siendo Segoe UI, o sea que alli no cambia nada. */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  min-height: 100vh;
  /* Plano. Antes habia un radial-gradient de --bg-2 a --bg-1 con
     `background-attachment: fixed`; las dos cosas se fueron juntas: sobre un
     color plano el `fixed` no pinta nada distinto y en Safari de iOS obliga al
     navegador a mantener una capa de composicion aparte, o sea que dejaba de
     ser gratis y dejaba de servir para nada.
     Efecto lateral bueno: el <meta name="theme-color"> de index.html dice
     #16041f y ahora por fin es EXACTO — antes arriba de la pagina estaba el
     halo del degradado y la barra del navegador en Android no empalmaba. */
  background: var(--bg-1);
}

/* ================= HEADER ================= */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 24px;
  background: rgba(0, 0, 0, 0.45);
  border-bottom: 1px solid var(--line-soft);
}
/* The side was a fixed 80px box holding only the sound control. The badge
   counter now shares it, so it has to be a ROW — as a plain block the two
   stacked and the header grew to 106px. flex:1 on both sides keeps the logo
   centred without either side needing a hard-coded width. */
.topbar-side {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.topbar-side:last-child { justify-content: flex-end; }
.logo { display: flex; align-items: center; }
.logo-img {
  width: clamp(220px, 32vw, 340px);
  height: auto; /* preserves the source aspect ratio — never stretched */
  display: block;
}

/* ================= SCREENS ================= */
.screen { display: none; }
.screen.active { display: block; }

.tagline {
  text-align: center;
  margin: 40px 16px 28px;
  font-size: 1.15rem;
  color: var(--text-dim);
}

/* ================= HOME CARDS ================= */
/* Fixed 3-per-row on desktop, narrowing to 2 then 1 on smaller screens.
   Flex + a max-width cap rather than a grid: grid tracks are fixed columns, so
   a final row that doesn't fill them stays left-aligned in its tracks. Flex
   centres EVERY row, including a short last one. The per-breakpoint max-width
   is what pins the column count, so this keeps the fixed layout the grid gave
   us instead of flex-wrap's usual variable count.
   Cap = (cards x 230) + gaps + the 32px of horizontal padding (border-box). */
.card-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 28px;
  padding: 0 16px 60px;
  max-width: 778px; /* 3x230 + 2x28 + 32 */
  margin-inline: auto;
}
@media (max-width: 860px) {
  .card-grid { max-width: 520px; } /* 2x230 + 28 + 32 */
}
@media (max-width: 540px) {
  .card-grid { max-width: 262px; } /* 230 + 32 */
}

.game-card {
  position: relative;
  width: 230px;
  /* 🚨 SIN MARCO Y SIN FONDO: desde el 2026-09-11 el borde, el relleno y la
     sombra viven en .card-art, o sea que el marco rodea SOLO la portada y el
     PLAY y el nombre se quedan fuera. La tarjeta sigue siendo un unico
     <button>, asi que el area clicable no cambia — lo unico que cambia es
     donde se pinta la caja.
     `overflow: visible` es lo que permite que la medalla y el badge asomen por
     fuera; NO lo quites. */
  border: none;
  border-radius: 14px;
  background: transparent;
  cursor: pointer;
  overflow: visible;
  padding: 0;
  color: var(--text);
  /* Solo `transform`: la sombra ya no esta aqui, esta en .card-art, que tiene
     su propia transicion. Dejar aqui un `box-shadow` en la transicion seria
     animar algo que no existe. */
  transition: transform 0.15s ease;
}
/* Lo que se levanta es la TARJETA ENTERA — portada, PLAY y nombre — no solo el
   marco. Por eso el `transform` se queda en .game-card y solo las sombras bajan
   a .card-art. */
.game-card:hover {
  transform: translateY(-4px);
}
/* 🚨 LAS DOS SOMBRAS, y en este orden. `box-shadow` NO se acumula entre reglas:
   lo que declare el hover SUSTITUYE a lo de reposo. Si aqui solo va el halo
   morado, al pasar el raton la tarjeta sube 4px y a la vez PIERDE su sombra, o
   sea que se lee como si cayera — justo lo contrario de lo que se busca.
   (Antes esto colgaba de .game-card; se mudo aqui con el marco.) */
.game-card:hover .card-art {
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55),
              0 0 22px rgba(155, 48, 255, 0.55);
}

/* 🚨 SIN USAR A PROPOSITO desde el 2026-09-12. La pastilla verde «NEW» estaba
   en Word Lab Radio, Bomb Word y Emoji Bomb, y las tres se quitaron: ya no son
   novedad. La regla SE CONSERVA porque Omar quiere volver a ponerla cuando
   salga un juego nuevo — no es basura que limpiar dentro de tres meses.
   (Queda una en el markup, la de Star Party, dentro de su bloque comentado.)

   🚨 NO CONFUNDIR con el contador de logros. En este proyecto «badge» son dos
   cosas distintas y un buscar-y-reemplazar se lleva las dos:
     .badge ................ esta pastilla «NEW». Es la que esta sin usar.
     .badge-counter y sus
     -icon / -text ......... el contador X/48 de la cabecera. VIVO.
     .ach-badge ............ cada medalla de la pantalla de logros. VIVO.
     assets/badges/ ........ los PNG de esas medallas. VIVOS.
   Cuando una verificacion dice «48 badges», habla de los LOGROS. */
.badge {
  position: absolute;
  top: -12px;
  left: -10px;
  background: #23d160;
  color: #06240f;
  font-weight: 800;
  font-size: 0.8rem;
  padding: 4px 12px;
  border-radius: 8px;
  z-index: 2;
}

/* 🚨 AQUI ESTA EL MARCO. Desde el 2026-09-11 el borde, el relleno y la sombra
   son de la portada, no de la tarjeta: el PLAY y el nombre se quedan fuera.
   Medido contra el fondo teal --bg-1 #131B20:

       relleno --panel #1A252C .... 1,12 : 1
       borde --panel-border ....... 2,09 : 1
       borde contra el relleno .... 1,87 : 1

   O sea que EL RELLENO NO SEPARA NADA: la caja se dibuja con el borde y con la
   sombra, no con su color. Por eso el borde es de 4px y bajarlo la haria
   desaparecer.

   `border-radius` LAS CUATRO esquinas: antes era `14px 14px 0 0` porque abajo
   se apoyaba en el pie, y ahora es una caja suelta.

   Y la `transition` NO es decorativa: el halo del hover vive en esta regla, asi
   que sin ella apareceria de golpe en vez de en 150 ms. .game-card ya no puede
   animarla por nosotros porque alli ya no hay ninguna sombra. */
.card-art {
  position: relative; /* positioning context for absolutely-placed decorative art */
  /* CUADRADA. Con `aspect-ratio` y no con un `height` fijo: a 230px de alto
     dejaria de ser cuadrada en movil, donde la tarjeta mide 160 de ancho. */
  height: auto;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden; /* nothing decorative may ever spill past the card edge */
  border-radius: 14px;
  background: var(--panel);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
  transition: box-shadow 0.15s ease;
}
/* 🚨 EL MARCO ES UNA CAPA ENCIMA, no un `border`, y el motivo no es estetico.
   Con `border: 4px` la imagen se dimensiona contra la caja de RELLENO — 222 px,
   no 230 — asi que todos los --art-scale salian un 3,5% cortos. Con la capa, la
   imagen ocupa los 230 completos y las cuentas cuadran.
   De paso: si algun dia una portada trae su propio marco dibujado, esta capa lo
   TAPA en vez de duplicarlo.

   `pointer-events: none` NO ES OPCIONAL: la tarjeta entera es un <button> y sin
   eso la capa se come los clics del centro de la portada. */
.card-art::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 4px solid var(--panel-border);
  border-radius: 14px;
  pointer-events: none;
  z-index: 2;
}

/* reusable pattern for After Effects-exported card art (1200x1200 WebP,
   transparent, includes the game's title/glow already baked in). Opt-in via
   this modifier class so it doesn't affect the hand-built CSS/SVG previews
   still used by the other cards — swap only the <img> src/srcset per game.
   No height/aspect-ratio override here on purpose: hereda la caja de
   .card-art, que desde el 2026-09-11 es CUADRADA por `aspect-ratio: 1/1` en
   los dos breakpoints, asi que las catorce tarjetas mantienen la misma forma
   — lo unico que cambia es el padding, porque object-fit:contain en el <img>
   ya se encarga del aire interior. */
.card-art.card-art-asset {
  padding: 0;
}
.card-art-img {
  display: block;
  position: absolute; /* sized off .card-art's actual box (it's position:relative), not the
                          flex height:100% percentage, which resolves inconsistently across
                          browsers when the card is height:auto (mobile) — that mismatch was
                          making some image cards render a few px taller than others. */
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain; /* never stretched/deformed, whatever the source's real proportions */
  object-position: center;
  /* per-card nudge/scale knobs — see the --art-shift-y / --art-scale block below
     for what each one does and how to tune it. Both default to a no-op so any
     card without its own modifier class renders exactly as object-fit intended. */
  transform: translateY(var(--art-shift-y, 0%)) scale(var(--art-scale, 1));
  transform-origin: center center;
}
/* set by app.js when the PNG 404s (e.g. art not uploaded yet): drop the broken
   image so the card shows only its own gradient instead of the browser's
   broken-image icon + alt text. Needs the doubled class to outrank the
   display:block above. */
.card-art-img.card-art-img-failed { display: none; }

/* ---- la escala del arte de las tarjetas ----
   --art-scale   : zoom uniforme del dibujo en su sitio (scale(), centrado).
                   1 = el tamano que calcula object-fit: contain.
   --art-shift-y : desplazamiento vertical dentro de la caja. Los dos se leen
                   en .card-art-img, mas arriba.

   🚨 UNA SOLA REGLA PARA LAS CATORCE, y no es pereza: desde el 2026-09-12 las
   catorce portadas son ilustraciones que COMPARTEN REJILLA — el titulo empieza
   al 15,4% del alto y su altura de mayuscula es el 5,2%, identico en las
   catorce. Una escala vale para todas.

   Corolario practico, y es la razon de que esto sea una regla y no catorce:
   SI UNA TARJETA SE VE MAS GRANDE O MAS PEQUENA QUE SUS VECINAS, EL FALLO ESTA
   EN EL CSS, NO EN EL ARCHIVO. Ya paso: aqui habia nueve modificadores por
   tarjeta (.card-art-radio, .card-art-waffle...) de cuando cada ilustracion
   tenia su propia geometria, y al llegar las portadas nuevas quedaron vivos un
   `emojibomb: 0.82` y un `radio: 1.15` — tres tamanos distintos con archivos
   identicos, y parecia culpa de los archivos. Por eso los nueve se BORRARON en
   vez de igualarse: con una sola regla el fallo no se puede repetir.

   Las clases .card-art-NOMBRE siguen en el markup de las catorce como gancho
   uniforme, pero YA NO LAS LEE NADIE. Si algun dia una portada necesita un
   valor propio, ese es el sitio — y habra que justificar por que esa se sale
   de la rejilla comun.

   ⚠️ El 1.15 es DELIBERADO: hace que el marco muerda un poco el dibujo. A esa
   escala tres portadas sobresalen y `overflow: hidden` se las come — Emoji
   Match (2,9 izq / 2,7 der / 4,0 abajo), Word Links (4,0 abajo) y Spot the
   Error (3,6 abajo). Es lo que Omar quiere, igual que ya muerde la bomba de
   Bomb Word. No bajarlo ni recentrar nada. */
.card-art-img { --art-scale: 1.15; --art-shift-y: 0; }

.mini-sentence { text-align: center; font-size: 0.85rem; color: var(--text-dim); line-height: 1.7; padding: 0 14px; }
.mini-blank { color: var(--accent-bright); font-weight: 800; letter-spacing: 2px; }
.mini-error { color: #ff7b7b; }
.mini-error u { text-decoration-color: #ff4d4d; text-decoration-thickness: 2px; }

.mini-contexto { text-align: center; width: 80%; }
.mini-secret { font-weight: 900; font-size: 1.3rem; letter-spacing: 4px; color: var(--accent-bright); margin-bottom: 12px; }
.mini-bar { height: 10px; border-radius: 5px; background: rgba(255, 255, 255, 0.12); overflow: hidden; margin-bottom: 12px; }
.mini-bar-fill { width: 60%; height: 100%; border-radius: 5px; background: linear-gradient(90deg, var(--accent), var(--accent-bright)); }
.mini-trait { font-size: 0.78rem; color: var(--text-dim); }

/* Fuera del marco desde el 2026-09-11, asi que sin fondo ni esquinas propias:
   el velo `rgba(0,0,0,.35)` y el `border-radius: 0 0 14px 14px` existian para
   cerrar la tarjeta por abajo, y ya no hay nada que cerrar. El padding lateral
   tambien sobra — el texto se alinea con el borde del marco. */
.card-footer {
  padding: 12px 0 0;
  text-align: center;
}
.card-play {
  display: block;
  background: var(--accent);
  color: #fff;
  font-weight: 800;
  letter-spacing: 1px;
  border-radius: 8px;
  padding: 8px 0;
  margin-bottom: 8px;
}
.game-card:hover .card-play { background: var(--accent-bright); }
.card-name { font-weight: 700; font-size: 0.95rem; }

/* ================= INTRO MODAL ================= */
.overlay {
  position: fixed;
  inset: 0;
  background: var(--veil);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 10;
}
.overlay.hidden { display: none; }

/* Fundido del velo, SOLO en el modal de inicio de partida. .overlay la comparten
   OCHO elementos (los cinco paneles finales, el modal de INFO del test y el
   pop-up de salida), asi que la regla no puede ser generica.
   Se anima el COLOR DE FONDO y no la opacidad del overlay: asi el velo se funde
   y la tarjeta de encima entra a plena opacidad con su cascada, sin que una
   animacion pise a la otra. Se rearma sola en cada apertura porque el elemento
   pasa de display:none a flex. */
@keyframes introVeil {
  /* `transparent` y no un rgba a pelo: la interpolacion es premultiplicada,
     asi que no tinie el fundido, y ademas asi este keyframe no depende del
     tema — el `to` ya lo hace por el token. */
  from { background-color: transparent; }
  to   { background-color: var(--veil); }
}
#intro-overlay:not(.hidden) { animation: introVeil 0.2s ease both; }

.intro-card {
  position: relative;
  width: 100%;
  max-width: 520px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 22px;
  padding: 36px 30px 30px;
  text-align: center;
  box-shadow: var(--panel-glow);
}
.intro-card h2 { font-size: 2.2rem; letter-spacing: 2px; margin-bottom: 14px; }
.intro-desc { color: var(--text-dim); margin-bottom: 26px; line-height: 1.5; }

.close-btn {
  position: absolute;
  top: 10px; right: 16px;
  background: none; border: none;
  color: var(--text-dim);
  font-size: 1.8rem;
  cursor: pointer;
}
.close-btn:hover { color: #fff; }

/* language toggle: pill with two flag circles + a sliding highlight thumb.
   Lives once in the shared intro modal, so it's automatically present for
   every game that opens through openIntro() and absent for Star Party
   (which uses its own #screen-starparty setup, never this modal). */
.lang-toggle {
  position: absolute;
  top: 14px;
  left: 16px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: linear-gradient(135deg, rgba(168, 85, 247, 0.4), rgba(236, 72, 220, 0.35));
  border: 1px solid rgba(216, 70, 239, 0.65);
  box-shadow: 0 0 18px rgba(216, 70, 239, 0.45), inset 0 0 10px rgba(168, 85, 247, 0.2);
}
.lang-thumb {
  position: absolute;
  top: 4px;
  left: 4px;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg, #b95cff, #ec4ce0);
  box-shadow: 0 0 14px rgba(236, 72, 220, 0.95), 0 0 26px rgba(168, 85, 247, 0.6);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}
.lang-toggle[data-active="es"] .lang-thumb { transform: translateX(42px); }
.lang-btn {
  position: relative;
  z-index: 1;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: none;
  background: none;
  padding: 0;
  overflow: hidden;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.3s ease, transform 0.15s ease;
}
.lang-btn svg { display: block; width: 100%; height: 100%; }
.lang-btn.active { opacity: 1; }
.lang-btn:hover { transform: scale(1.08); }

.select-label { color: var(--text-dim); font-size: 0.9rem; margin-bottom: 8px; text-align: left; }
.option-row {
  display: flex;
  justify-content: space-around;
  gap: 8px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}
.opt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  font-weight: 800;
  font-size: 0.85rem;
  letter-spacing: 1px;
  cursor: pointer;
}
.opt input { display: none; }
.opt .box {
  width: 26px; height: 26px;
  border-radius: 6px;
  background: #2e2e33;
  display: inline-block;
  position: relative;
  transition: background 0.15s;
}
.opt input:checked + .box { background: #e9e9ee; }
.opt input:checked + .box::after {
  content: "";
  position: absolute;
  inset: 6px;
  border-radius: 50%;
  background: #555;
}

.primary-btn {
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 10px;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 1px;
  padding: 12px 46px;
  cursor: pointer;
  transition: background 0.15s;
}
.primary-btn:hover { background: var(--accent-bright); }
.primary-btn.small { padding: 10px 22px; font-size: 0.95rem; }
/* START while its game's data bank is still downloading (DataLoader in
   app.js). Disabled so the game can never start on an undefined global. */
.primary-btn:disabled { cursor: not-allowed; }
#intro-start.is-loading { background: #5b3a78; color: var(--text-dim); }

/* ================= GAME SHARED ================= */
.game-topline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  max-width: 640px;
  margin: 0 auto;
}
.back-btn {
  background: none;
  border: 1px solid var(--panel-border);
  color: var(--text);
  border-radius: 8px;
  padding: 6px 14px;
  cursor: pointer;
  font-size: 0.9rem;
}
.back-btn:hover { background: rgba(155, 48, 255, 0.25); }
.game-meta { color: var(--text-dim); font-size: 0.85rem; letter-spacing: 1px; text-transform: uppercase; }

/* sound control: a speaker icon that opens a small volume popover — appears in
   the header (site-wide) and again on every game screen that has sound (Bomb
   Word, Emoji Bomb, Fill in the Blanks, Word Links, Spot the Error, Waffle,
   Connections, Wordle, Is It A Real Word, Emoji Match, Impostor, Strands).
   All instances share one localStorage-backed state, so touching any one of
   them updates every icon and slider on the page at once. */
.sfx-control {
  position: relative; /* anchors the popover to THIS button, not the page */
}
/* the single instance lives in the header and is only shown while the home
   screen is active — toggled by showScreen() in app.js */
.sfx-control.hidden { display: none; }
.mute-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  flex-shrink: 0;
  border-radius: 50%; /* not visible on its own anymore, but keeps the
    hover glow and focus ring round instead of a square outline */
  border: none;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  transition: opacity 0.15s, box-shadow 0.15s;
}
.mute-btn:hover, .mute-btn:focus-visible { opacity: 0.82; box-shadow: 0 0 0 6px rgba(155, 48, 255, 0.18); }
.mute-btn:focus-visible { outline: 2px solid var(--accent-bright, #d846ef); outline-offset: 2px; }
.mute-icon { font-size: 1.1rem; line-height: 1; }

/* The artwork IS the button now: assets/games/VOLUME.webp already draws its
   own filled circular disc (measured centered in its 256x256 canvas, radius
   ~95px), so the CSS no longer draws a second circle around it — that was
   the "two circles that don't match" bug. Filling the full 44px tap target
   lets the art's own disc be the only circle on screen. */
.mute-btn-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none; /* clicks belong to the button, never the image */
  transition: opacity 0.15s, filter 0.15s;
}
/* muted state: the trigger emoji used to flip to 🔇, which an image can't do.
   The class is set by muteRenderIcons() alongside the existing icon swap. */
.sfx-control.is-muted .mute-btn-img { opacity: 0.4; filter: grayscale(1); }

/* The side slot now right-aligns its own children (justify-content:flex-end),
   so the old margin-left:auto is not just redundant — with the badge counter
   beside it, it would shove the counter to the far left of the slot. */
.topbar-side .sfx-control { margin-left: 0; }

/* the popover: sits BELOW the trigger button, right-edge aligned to it so it
   can never run off the right side of a narrow phone screen. Fixed width,
   high z-index so it always sits above game content underneath it. */
.sfx-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 140px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid var(--line-soft);
  background: var(--panel);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  z-index: 50;
}
.sfx-panel.hidden { display: none; }

/* the mute toggle lives inside the popover now — compact icon + label, not a
   full circular button, so it doesn't compete with the slider for the 140px */
.sfx-mute-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  padding: 0;
  border: none;
  background: none;
  color: var(--text);
  cursor: pointer;
  min-width: 32px;
  min-height: 32px;
}
.sfx-mute-toggle .mute-icon { font-size: 1rem; }
.sfx-mute-label { display: none; } /* icon alone is enough at this size; kept in the DOM for screen readers via aria-label on the button */

/* horizontal slider filling the rest of the 140px panel */
.sfx-volume {
  flex: 1;
  min-width: 0;
  height: 32px;
  accent-color: var(--accent-bright, #d846ef);
  cursor: pointer;
  background: transparent;
}

.game-message { text-align: center; min-height: 24px; margin: 10px 0; font-weight: 700; color: var(--accent-bright); }

.end-panel {
  max-width: 420px;
  margin: 24px auto;
  padding: 26px;
  text-align: center;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
}
.end-panel.hidden { display: none; }
.end-title { font-size: 1.5rem; font-weight: 800; margin-bottom: 10px; }
.end-word { color: var(--text-dim); margin-bottom: 20px; }
.end-word strong { color: #fff; text-transform: uppercase; letter-spacing: 1px; }
.end-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* result modal over the wordle board: darker backdrop for focus */
.result-overlay { background: rgba(0, 0, 0, 0.65); }
.result-overlay .end-panel { margin: 0; width: 100%; max-width: 420px; box-shadow: var(--panel-glow); }

/* ---- end-of-game reveal: staggered pop-in, shared by EVERY game ----
   Generalised from the CONNECTIONS solved-group cards, which used chipIn
   with no delay (all four landed at once). Here every direct child of an
   .end-panel — title, score, word list, buttons — animates in sequence.
   Pure CSS on purpose: the panels go display:none -> visible on each game
   over, which restarts the animation by itself, so no game needs a single
   line of JS to opt in. Six steps covers the deepest panel (Strands /
   Emoji Match / Hear It); anything beyond simply lands with the 6th.  */
@keyframes endPop {
  from { opacity: 0; transform: translateY(10px) scale(0.85); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.end-panel > * { animation: endPop 0.35s ease both; }
.end-panel > *:nth-child(1) { animation-delay: 0ms; }
.end-panel > *:nth-child(2) { animation-delay: 60ms; }
.end-panel > *:nth-child(3) { animation-delay: 120ms; }
.end-panel > *:nth-child(4) { animation-delay: 180ms; }
.end-panel > *:nth-child(5) { animation-delay: 240ms; }
.end-panel > *:nth-child(n + 6) { animation-delay: 300ms; }

/* STAR PARTY is deliberately excluded: #sp-end reuses .end-panel with the
   same markup, so without this it would inherit the cascade too. */
#sp-end .end-panel > * { animation: none; }

/* accessibility: honour a reduced-motion preference by showing the panel
   fully formed instead of animating it in */
@media (prefers-reduced-motion: reduce) {
  .end-panel > * { animation: none; }
}

/* ================= WORDLE HINTS ================= */
.hint-box {
  max-width: 560px;
  margin: 6px auto 0;
  padding: 0 16px;
  text-align: center;
}
.hint-line {
  display: inline-block;
  margin: 4px 6px;
  padding: 7px 16px;
  border-radius: 20px;
  background: var(--tile);
  border: 1px solid var(--line-soft);
  color: var(--text-dim);
  font-size: 0.9rem;
}
.hint-line:empty { display: none; }
.hint-line.hidden { display: none; }
.hint-reveals.hidden { display: none; }

.end-meaning {
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
  margin: -8px 0 20px;
}

/* ================= WORDLE BOARD ================= */
.board {
  display: grid;
  gap: 6px;
  justify-content: center;
  margin: 18px auto 8px;
}
.board-row { display: grid; gap: 6px; }
.tile {
  width: 58px; height: 58px;
  border: 2px solid var(--tile-border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.8rem;
  font-weight: 800;
  text-transform: uppercase;
  color: #fff;
  background: transparent;
  transition: background 0.25s, border-color 0.25s;
}
.tile.filled { border-color: #aaa; }
.tile.green  { background: var(--tile-green);  border-color: var(--tile-green); }
.tile.yellow { background: var(--tile-yellow); border-color: var(--tile-yellow); }
.tile.gray   { background: var(--tile-gray);   border-color: var(--tile-gray); }

/* Shrink tiles for long words / small screens */
.board.compact .tile { width: 42px; height: 42px; font-size: 1.3rem; }
.board.tiny .tile    { width: 32px; height: 32px; font-size: 1rem; }

/* ================= WORDLE ACTION BUTTONS ================= */
.board-actions {
  display: flex;
  justify-content: space-between;
  max-width: 560px;
  margin: 4px auto 10px;
  padding: 0 12px;
}
.action-btn {
  border: none;
  border-radius: 10px;
  font-weight: 800;
  font-size: 0.85rem;
  letter-spacing: 1px;
  padding: 10px 18px;
  cursor: pointer;
  color: #1a1a1a;
  transition: filter 0.15s, transform 0.1s;
}
.action-btn:hover { filter: brightness(1.15); }
.action-btn:active { transform: scale(0.96); }
.action-btn:disabled { opacity: 0.45; cursor: default; }
.hint-btn { background: #f5c518; }       /* yellow */
.forfeit-btn { background: #e0442e; color: #fff; } /* red */

/* letters revealed by the hint button */
.hint-reveals {
  display: flex;
  gap: 5px;
  justify-content: center;
  margin: 8px auto 0;
}
.hint-tile {
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800;
  text-transform: uppercase;
  color: #fff;
  border-radius: 4px;
  background: transparent;
  border: 1.5px dashed rgba(255, 255, 255, 0.25);
}
.hint-tile.revealed { background: var(--tile-green); border: none; }

/* ================= KEYBOARD ================= */
.keyboard {
  max-width: 560px;
  margin: 10px auto 40px;
  padding: 0 8px;
  user-select: none;
}
.kb-row { display: flex; justify-content: center; gap: 6px; margin-bottom: 8px; }
.key {
  flex: 1;
  max-width: 48px;
  height: 56px;
  background: var(--key-bg);
  color: #fff;
  border: none;
  border-radius: 5px;
  font-weight: 800;
  font-size: 1rem;
  cursor: pointer;
  text-transform: uppercase;
}
.key.wide { max-width: 80px; flex: 1.6; font-size: 0.75rem; }
.key.green  { background: var(--tile-green); }
.key.yellow { background: var(--tile-yellow); }
.key.gray   { background: var(--tile-gray); }
.key:active { filter: brightness(1.2); }

/* ================= FILL IN THE BLANKS ================= */
.blanks-wrap {
  max-width: 560px;
  margin: 20px auto;
  padding: 30px 24px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
  text-align: center;
}

/* FILL IN THE BLANKS composed logo header (replaces plain title on the game's
   intro screen): FILL IN → 4-cell icon (1st solved blue, 2nd raised) → BLANKS */
/* 🚨 ESTO NO ES CODIGO MUERTO aunque la tarjeta de la portada ya no lo use.
   Desde el 2026-09-12 Fill in the Blanks es una imagen (FILLBLANKS.png), pero
   applyIntroLanguage() en app.js (~465) RECONSTRUYE este logotipo a mano para
   la cabecera de su modal de inicio — y lo vuelve a hacer cada vez que el
   jugador cambia de idioma con la bandera. Si se borra .fib-logo,
   .fib-logo-word, .fib-logo-cells o .fib-cell (con .done y .up), el modal de
   Fill in the Blanks se queda SIN TITULO, en los dos idiomas.
   Mismo caso que .mini-radio con la radio. Y ojo: .sp-slot-fib-cell de Star
   Party es un primo calcado pero OTRA clase — no se toca. */
.fib-logo { display: inline-flex; flex-direction: column; align-items: center; gap: 8px; line-height: 1; }
/* no text-shadow: the wordmark reads flat/crisp. The cells below keep their
   box-shadow glow — only the lettering was de-glowed. */
.fib-logo-word {
  font-weight: 900; letter-spacing: 4px; font-size: 1.5rem; color: #fff;
}
.fib-logo-cells { display: flex; align-items: flex-end; gap: 7px; height: 46px; }
.fib-cell {
  width: 36px; height: 40px; border-radius: 7px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 900; font-size: 1.3rem; color: #fff;
  border: 2px solid rgba(255, 255, 255, 0.55);
  background: rgba(255, 255, 255, 0.06);
}
.fib-cell.done {
  background: #3b82f6; border-color: #60a5fa; color: #eaf2ff;
  box-shadow: 0 0 12px rgba(59, 130, 246, 0.6);
}
/* the active/"being typed" cell sits slightly raised, with an accent glow */
.fib-cell.up {
  transform: translateY(-8px);
  border-color: var(--panel-border);
  box-shadow: 0 0 12px var(--border-glow);
}

/* generic intro-screen icon-above-title lockup — reused by IMPOSTOR (spy icon)
   and BOMB WORD (bomb icon), same composed-logo pattern as FILL IN THE BLANKS */
.intro-icon-logo { display: inline-flex; flex-direction: column; align-items: center; gap: 8px; line-height: 1; }
.intro-icon-logo .mini-impo-spy { filter: drop-shadow(0 0 10px rgba(214, 200, 255, 0.65)) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); }
.intro-icon-logo-emoji { font-size: 2.6rem; filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.5)); }
.intro-icon-logo-word {
  font-weight: 900; letter-spacing: 3px; font-size: 1.5rem; color: #fff;
  text-shadow: 0 0 12px rgba(168, 85, 247, 0.7);
}
.blanks-wrap.hidden { display: none; }
.blanks-progress { color: var(--text-dim); font-size: 0.85rem; letter-spacing: 2px; margin-bottom: 18px; }
.blanks-sentence { font-size: 1.3rem; line-height: 1.6; margin-bottom: 12px; }
.blanks-sentence .gap { color: var(--accent-bright); font-weight: 800; }
.blanks-dashes { font-size: 1.3rem; letter-spacing: 4px; color: var(--accent-bright); font-weight: 800; margin-bottom: 22px; }

.blanks-form { display: flex; flex-direction: column; gap: 16px; align-items: center; margin-bottom: 12px; }
.blanks-form.hidden { display: none; }

/* one box per letter — revealed letters are fixed, the rest are typed in place */
.letter-boxes {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  justify-content: center;
}
.letter-cell {
  width: 40px; height: 46px;
  border-radius: 8px;
  border: 1.5px solid var(--panel-border);
  background: var(--sunken);
  color: var(--text);
  font-size: 1.3rem;
  font-weight: 800;
  text-transform: uppercase;
  text-align: center;
  display: flex; align-items: center; justify-content: center;
}
input.letter-cell { outline: none; padding: 0; caret-color: var(--accent-bright); }
input.letter-cell:focus { border-color: var(--accent-bright); box-shadow: 0 0 8px rgba(185, 92, 255, 0.5); }
.letter-cell.fixed {
  background: rgba(155, 48, 255, 0.3);
  color: var(--accent-bright);
  border-color: var(--accent-bright);
}
@media (max-width: 480px) {
  .letter-cell { width: 30px; height: 38px; font-size: 1rem; border-radius: 6px; }
}

.blanks-options { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.blanks-options.hidden { display: none; }

/* meaning panel shown after a sentence is resolved (correct/incorrect/timeout) */
.option-btn {
  padding: 12px;
  border-radius: 10px;
  border: 1px solid var(--panel-border);
  background: var(--tile);
  color: var(--text);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}
.option-btn:hover { background: var(--tile-hover); }
.option-btn.correct { background: var(--tile-green); border-color: var(--tile-green); }
.option-btn.wrong   { background: #8d3030; border-color: #8d3030; }

/* ================= SPOT THE ERROR ================= */
.spot-wrap {
  max-width: 640px;
  margin: 20px auto;
  padding: 30px 24px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
  text-align: center;
}
.spot-wrap.hidden { display: none; }
.spot-instruction { color: var(--text-dim); margin-bottom: 20px; font-size: 1rem; }

/* phase 1 — clickable sentences */
.spot-sentences { display: flex; flex-direction: column; gap: 12px; }
.spot-sentences.hidden { display: none; }
.spot-sentence {
  padding: 14px 16px;
  border-radius: 12px;
  border: 1px solid var(--panel-border);
  background: var(--tile);
  color: var(--text);
  font-size: 1.05rem;
  line-height: 1.5;
  cursor: pointer;
  text-align: left;
  transition: background 0.15s, transform 0.1s, opacity 0.3s;
}
.spot-sentence:hover { background: var(--tile-hover); transform: translateX(4px); }
.spot-sentence.wrong { background: #8d3030; border-color: #8d3030; opacity: 0; pointer-events: none; }
.spot-sentence.found { background: var(--tile-green); border-color: var(--tile-green); }

/* phase 2 — clickable words */
.spot-words { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 6px; }
.spot-words.hidden { display: none; }
.spot-word {
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px dashed var(--panel-border);
  background: var(--sunken);
  color: var(--text);
  font-size: 1.05rem;
  cursor: pointer;
  transition: background 0.15s;
}
.spot-word:hover { background: var(--tile-hover); border-style: solid; }
.spot-word.wrong { background: #8d3030; border-color: #8d3030; border-style: solid; }
.spot-word.found { background: var(--tile-green); border-color: var(--tile-green); border-style: solid; }

/* explanation panel */
.spot-explain {
  margin-top: 20px;
  padding: 18px;
  border-radius: 12px;
  background: var(--tile);
  border: 1px solid var(--line-soft);
}
.spot-explain.hidden { display: none; }
.spot-explain-title { font-weight: 800; font-size: 1.1rem; margin-bottom: 10px; }
.spot-corrected { color: #7ee2a0; font-weight: 700; margin-bottom: 10px; }
.spot-explanation { color: var(--text-dim); line-height: 1.6; margin-bottom: 16px; }

/* ================= CONTEXTO ================= */
.contexto-wrap {
  max-width: 620px;
  margin: 20px auto;
  padding: 28px 24px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
}
.contexto-wrap.hidden { display: none; }
.contexto-title { text-align: center; font-size: 1.15rem; font-weight: 700; margin-bottom: 18px; }

/* progress bar */
.contexto-bar-row { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.contexto-bar {
  flex: 1;
  height: 16px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid var(--line-soft);
  overflow: hidden;
}
.contexto-bar-fill {
  width: 0%;
  height: 100%;
  border-radius: 8px;
  background: linear-gradient(90deg, var(--accent), var(--accent-bright));
  transition: width 0.4s ease;
}
.contexto-points { font-weight: 800; font-size: 0.9rem; color: var(--accent-bright); white-space: nowrap; }

/* the two inputs */
.contexto-form { margin-bottom: 14px; }
.contexto-form.word-form {
  padding: 12px;
  border-radius: 12px;
  background: rgba(245, 197, 24, 0.07);
  border: 1px dashed rgba(245, 197, 24, 0.4);
}
.contexto-label { display: block; font-size: 0.85rem; color: var(--text-dim); margin-bottom: 6px; font-weight: 700; }
.contexto-counter { color: var(--accent-bright); }
.contexto-input-row { display: flex; gap: 8px; }
.contexto-input-row input {
  flex: 1;
  min-width: 0;
  padding: 11px 14px;
  border-radius: 10px;
  border: 1px solid var(--panel-border);
  background: var(--sunken);
  color: var(--text);
  font-size: 1rem;
  outline: none;
}
.contexto-input-row input:focus { border-color: var(--accent-bright); }
.contexto-input-row input:disabled { opacity: 0.4; }
.primary-btn.gold { background: #f5c518; color: #1a1a1a; }
.primary-btn.gold:hover { background: #ffd83d; }

/* autocomplete dropdown */
.autocomplete-anchor { position: relative; }
.suggest-box {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 5;
  background: var(--panel);
  border: 1px solid var(--accent-bright);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
}
.suggest-box.hidden { display: none; }
.suggest-item {
  display: block;
  width: 100%;
  padding: 9px 14px;
  background: none;
  border: none;
  color: var(--text);
  font-size: 0.95rem;
  text-align: left;
  cursor: pointer;
}
.suggest-item:hover, .suggest-item.active { background: rgba(155, 48, 255, 0.3); }
.suggest-item b { color: var(--accent-bright); }

/* hint / forfeit buttons */
.contexto-actions {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}

/* history of guesses — 4 result levels */
.contexto-history { list-style: none; margin-top: 6px; max-height: 300px; overflow-y: auto; }
.contexto-history li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 14px;
  border-radius: 9px;
  margin-bottom: 6px;
  font-size: 0.95rem;
  background: rgba(255, 255, 255, 0.05);
  color: var(--text-dim);
}
.contexto-history li.hit {           /* strong: trait found (+8) */
  background: rgba(83, 141, 78, 0.45);
  border: 1px solid var(--tile-green);
  color: #c8f0c3;
  font-weight: 700;
}
.contexto-history li.hit-soft {      /* weak: relatedGreen (+3) */
  background: rgba(83, 141, 78, 0.18);
  border: 1px solid rgba(83, 141, 78, 0.7);
  color: #9fcf9a;
}
.contexto-history li.close {         /* relatedYellow: "you're close" */
  background: rgba(230, 138, 46, 0.15);
  border: 1px solid rgba(230, 138, 46, 0.65);
  color: #f0b071;
}
.contexto-history li.dup { background: rgba(245, 197, 24, 0.12); border: 1px solid rgba(245, 197, 24, 0.4); color: #f0d47a; }
.contexto-history li.miss { border: 1px solid rgba(141, 48, 48, 0.6); color: #d99a9a; }
.contexto-history .pts { font-weight: 800; white-space: nowrap; }

/* end-of-game trait reveal */
.contexto-reveal { list-style: none; text-align: left; margin: 0 auto 20px; max-width: 340px; }
.contexto-reveal li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 12px;
  border-radius: 8px;
  margin-bottom: 5px;
  font-size: 0.9rem;
  background: var(--tile);
  color: var(--text-dim);
}
.contexto-reveal li span:last-child { color: #fff; font-weight: 700; }
.contexto-reveal li.was-found { background: rgba(83, 141, 78, 0.3); }

/* ===== shared click-to-expand meaning (Real Word, Waffle, Connections) ===== */
/* smooth open/close via max-height + opacity, ~0.3s to match other games */
.rw-expandable,
.wf-word-item {
  display: block;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}
.rw-row-head,
.wf-word-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.rw-row-head .rw-tag { color: var(--text-dim); font-weight: 600; }
.wf-word-head span:first-child { color: var(--text-dim); }
.wf-word-value { color: #fff; font-weight: 700; letter-spacing: 0.5px; }
.rw-row-head::after,
.wf-word-head::after {
  content: "▾";
  margin-left: 6px;
  opacity: 0.55;
  transition: transform 0.3s ease;
}
.rw-expandable.open .rw-row-head::after,
.wf-word-item.open .wf-word-head::after { transform: rotate(180deg); }

.rw-meaning,
.wf-word-meaning {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  padding: 0 12px;
  font-size: 0.85rem;
  line-height: 1.35;
  color: var(--text);
  background: rgba(0, 0, 0, 0.22);
  transition: max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease;
}
.rw-expandable.open .rw-meaning,
.wf-word-item.open .wf-word-meaning {
  max-height: 200px;
  opacity: 1;
  padding: 9px 12px;
}
.rw-meaning-fake { font-style: italic; opacity: 0.85; }

/* ===== IMPOSTOR end screen: one expandable bar per round =====
   Replaces the old flat "R1: <long explanation> — impostor: X   3 pts" line.
   Each bar is chevron | category title + impostor | points chip, and opens to
   show the round's 5 words with their meanings. Category titles come from
   impostor_labels.json, which is keyed by the same positional round_id as the
   Spanish explanations. */
.imp-rounds { list-style: none; margin: 0 auto 14px; padding: 0; max-width: 380px; text-align: left; }
.imp-round {
  border-radius: 10px;
  margin-bottom: 8px;
  overflow: hidden;
  /* per-result palette, consumed by the bar and its points chip below */
  --imp-bar: rgba(168, 85, 247, 0.22);
  --imp-bar-hover: rgba(168, 85, 247, 0.34);
  --imp-chip: rgba(126, 34, 206, 0.85);
  --imp-accent: var(--accent-bright, #d846ef);
}
/* lost rounds drop to neutral gray so the purple reads as "you got it" */
.imp-round.imp-lost {
  --imp-bar: rgba(148, 163, 184, 0.16);
  --imp-bar-hover: rgba(148, 163, 184, 0.26);
  --imp-chip: rgba(71, 85, 105, 0.9);
  --imp-accent: rgba(203, 213, 225, 0.75);
}

.imp-round-head {
  display: flex;
  align-items: stretch;
  gap: 0;
  width: 100%;
  padding: 0;
  border: none;
  background: var(--imp-bar);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.15s;
}
.imp-round-head:hover { background: var(--imp-bar-hover); }
.imp-round-head:focus-visible { outline: 2px solid var(--accent-bright, #d846ef); outline-offset: -2px; }

/* chevron sits at the far left and rotates on open */
.imp-round-chev {
  display: flex;
  align-items: center;
  padding: 0 4px 0 10px;
  font-size: 0.8rem;
  color: var(--imp-accent);
  transition: transform 0.3s ease;
  flex-shrink: 0;
}
.imp-round.open .imp-round-chev { transform: rotate(180deg); }

/* title + impostor line, takes the remaining width */
.imp-round-main { flex: 1; min-width: 0; padding: 9px 10px; }
.imp-round-title {
  display: block;
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: #fff;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.imp-round-impostor {
  display: block;
  margin-top: 2px;
  font-size: 0.72rem;
  color: var(--text-dim);
  overflow-wrap: anywhere;
}

/* points as a separate darker block flush against the right edge */
.imp-round-pts {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  padding: 0 12px;
  background: var(--imp-chip);
  color: #fff;
  font-weight: 800;
  font-size: 0.82rem;
  white-space: nowrap;
}

/* the drawer: same max-height/opacity transition the other games use */
.imp-round-words {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  padding: 0 12px;
  background: rgba(0, 0, 0, 0.26);
  transition: max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease;
}
.imp-round.open .imp-round-words { max-height: 460px; opacity: 1; padding: 8px 12px; }

.imp-word-row { padding: 5px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.07); }
.imp-word-row:last-child { border-bottom: none; }
.imp-word {
  display: block;
  font-weight: 800;
  font-size: 0.8rem;
  letter-spacing: 0.5px;
  color: #fff;
}
.imp-word-def { display: block; font-size: 0.78rem; line-height: 1.35; color: var(--text-dim); }
.imp-word-def.is-missing { font-style: italic; opacity: 0.75; }
/* the impostor of the round, called out inside the drawer */
.imp-word-row.is-impostor { border-left: 3px solid #ff8a8a; padding-left: 8px; margin-left: -8px; }
.imp-word-row.is-impostor .imp-word { color: #ff9a9a; }
.imp-word-tag {
  margin-left: 6px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: #ff9a9a;
  opacity: 0.9;
}

@media (max-width: 480px) {
  .imp-round-title { font-size: 0.88rem; }
  .imp-round-pts { padding: 0 9px; font-size: 0.78rem; }
}

/* Is It A Real Word? — purple/pink theme for the collapsed row and the
   expanded meaning panel (was flat gray/black). Correctness is now shown via
   a colored left border instead of a full green/red fill, so the row stays
   on-brand. Scoped to #rw-summary only — doesn't affect Waffle/Connections. */
#rw-summary.contexto-reveal li.rw-expandable {
  background: var(--tile-hover);
  border-left: 3px solid transparent;
  border-radius: 8px;
}
#rw-summary li.rw-expandable.was-found { border-left-color: #7ee2a0; }
#rw-summary li.rw-expandable.was-missed { border-left-color: #ff9a9a; }
#rw-summary .rw-row-head .rw-row-name { color: var(--text); font-weight: 700; }
#rw-summary .rw-meaning {
  background: var(--bg-78);
  border-top: 1px solid rgba(168, 85, 247, 0.35);
  color: var(--text);
}

/* Connections: expandable colored group card */
.conn-group-expandable { padding: 0; overflow: hidden; }
.conn-group-head {
  display: block;
  width: 100%;
  padding: 12px 14px;
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  text-align: center;
  cursor: pointer;
  position: relative;
}
.conn-group-head::after {
  content: "▾";
  position: absolute;
  right: 12px;
  top: 12px;
  opacity: 0.7;
  transition: transform 0.3s ease;
}
.conn-group-expandable.open .conn-group-head::after { transform: rotate(180deg); }
.conn-group-meanings {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  text-align: left;
  background: rgba(0, 0, 0, 0.25);
  transition: max-height 0.35s ease, opacity 0.3s ease, padding 0.3s ease;
  padding: 0 14px;
}
.conn-group-expandable.open .conn-group-meanings {
  max-height: 320px;
  opacity: 1;
  padding: 10px 14px;
}
.conn-meaning-row {
  display: flex;
  gap: 8px;
  padding: 4px 0;
  font-size: 0.85rem;
  line-height: 1.3;
}
.conn-meaning-word {
  font-weight: 800;
  min-width: 92px;
  flex-shrink: 0;
  letter-spacing: 0.3px;
}
.conn-meaning-def { opacity: 0.95; }
.conn-meaning-defwrap { flex: 1; min-width: 0; }

/* the per-word "🌐 ES" translate button used to live here. It is gone: word
   meanings now render directly in the language picked on each game's loading
   screen (see lookupDefinition in app.js), so there is nothing left to
   translate on demand. */
.meaning-extra:empty { display: none; }

/* ================= WORD LINKS ================= */
.mini-links { text-align: center; width: 85%; }
.mini-chips { display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; margin-bottom: 10px; }
/* ⚠️ `.mini-chip` (SINGULAR) se quedo sin uso el 2026-09-12: sus unicas ocho
   apariciones eran cuatro en la tarjeta de Word Links y cuatro en la de
   Impostor, y las dos pasaron a ilustracion. 0 apariciones en cualquier .js
   (comprobado: rdRenderList() monta .mini-radio y sus tres piezas, no chips).
   Se conserva igual que las tres de Impostor, pero que quede escrito.
   ⚠️ Y de paso: `.mini-links` y `.mini-chips` (PLURAL) de las dos lineas de
   arriba son OTRAS clases y llevan sin usarse desde antes — 0 apariciones en
   index.html ya en el commit 2a24c30, o sea que no las mato el cambio de hoy.
   Son restos de un dibujo de portada anterior. Misma familia de restos:
   .mini-impostor, .mini-sentence, .mini-blank, .mini-error, .mini-secret,
   .mini-bar, .mini-trait y .mini-contexto.
   🚨 Y desde el 2026-09-12 YA NO QUEDA NINGUNA .mini-* de portada en uso: las
   catorce tarjetas son ilustracion. Las unicas .mini-* vivas del archivo son
   las que reconstruye applyIntroLanguage() para las cabeceras de los modales
   (.mini-impo-spy) y .mini-radio, que monta rdRenderList() como dibujo de
   reserva de las historias de radio sin portada. */
.mini-chip {
  font-size: 0.62rem;
  font-weight: 800;
  padding: 4px 8px;
  border-radius: 12px;
  border: 1px solid var(--panel-border);
  color: var(--text-dim);
  background: var(--tile);
}

.wl-wrap {
  max-width: 620px;
  margin: 20px auto;
  padding: 28px 24px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
}
.wl-wrap.hidden { display: none; }

.wl-status {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.wl-type {
  font-weight: 800;
  font-size: 0.9rem;
  color: var(--accent-bright);
  padding: 4px 14px;
  border-radius: 14px;
  background: var(--tile);
  border: 1px solid var(--line-soft);
}
.wl-attempts { font-weight: 800; font-size: 0.9rem; color: var(--text); }

.wl-instruction { text-align: center; color: var(--text-dim); margin-bottom: 16px; }

/* clue chips */
.wl-clues {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-bottom: 18px;
}
.wl-chip {
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: 1px;
  padding: 12px 20px;
  border-radius: 14px;
  color: var(--text);
  background: var(--tile);
  border: 1px solid var(--panel-border);
  box-shadow: 0 0 12px rgba(155, 48, 255, 0.25);
}
.wl-chip.hint-chip {
  background: rgba(245, 197, 24, 0.12);
  border-color: rgba(245, 197, 24, 0.7);
  box-shadow: 0 0 12px rgba(245, 197, 24, 0.3);
  color: #f0d47a;
  animation: chipIn 0.35s ease;
}
@keyframes chipIn {
  from { opacity: 0; transform: translateY(10px) scale(0.85); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.wl-end-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.wl-reveal-word { color: #ff8a8a; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; }
#wl-next-btn.hidden { display: none; }

@media (max-width: 480px) {
  .wl-chip { font-size: 0.9rem; padding: 10px 14px; }
}

/* ================= IMPOSTOR ================= */
.mini-impostor { border-color: #ff6b6b; color: #ff9a9a; background: rgba(255, 77, 77, 0.12); }

/* 🚨 DOS SITUACIONES DISTINTAS EN ESTE BLOQUE, y conviene no confundirlas.
   Desde el 2026-09-12 la tarjeta de Impostor es una imagen (IMPOSTOR.png), asi
   que el dibujo de portada ya no se monta.

   - `.mini-impo-spy` SIGUE VIVA: applyIntroLanguage() (app.js ~478) reconstruye
     ese mismo SVG del espia para la cabecera del modal de Impostor, y lo
     repinta al cambiar de idioma. Borrarla deja el modal sin espia. La regla
     `.intro-icon-logo .mini-impo-spy` de mas arriba es justo la que le pone el
     resplandor ALLI.
   - `.mini-impo` (contenedor), `.mini-impo-word` y `.mini-impo-pills` SE
     QUEDARON SIN USO ese mismo dia: 0 apariciones en index.html y 0 en
     cualquier .js. Se conservan a proposito —dejar CSS de mas no rompe nada y
     borrar de menos rompe un modal que nadie mira hasta dentro de un mes— pero
     que quede escrito que hoy no las usa nadie.
   (`.mini-impostor` de la linea de arriba es OTRA clase, y tambien esta sin
   uso — pero desde antes de hoy, no por este cambio: 0 apariciones en
   index.html ya en el commit 2a24c30.) */
.mini-impo {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 100%;
}
.mini-impo-spy { filter: drop-shadow(0 0 10px rgba(214, 200, 255, 0.65)) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); }
/* IMPOSTOR wordmark between the spy and the pills — same style as the game's
   intro screen (.intro-icon-logo-word), sized to fit the smaller card */
.mini-impo-word {
  font-weight: 900; letter-spacing: 3px; font-size: 1.3rem; color: #fff;
  text-shadow: 0 0 12px rgba(168, 85, 247, 0.7);
}
.mini-impo-pills {
  position: relative;
  display: grid; grid-template-columns: auto auto; gap: 8px 10px;
  justify-content: center;
}
.mini-impo-pills .mini-chip {
  font-size: 0.72rem; padding: 6px 14px; border-radius: 14px;
  border-color: var(--panel-border); color: #fff;
  background: var(--tile);
}

/* 🚨 AQUI VIVIAN los dibujos de portada de SPOT THE ERROR y WORD LINKS
   (.mini-spot*, .mini-wl*) y el efecto compartido de la lupa (.mini-spot-lens
   / .mini-spot-glass). Se retiraron el 2026-09-12, cuando esas dos tarjetas
   pasaron a ilustracion (SPOTERROR.png y WORDLINKS.png).
   Comprobado antes de borrar, clase por clase: 0 apariciones en app.js y en el
   resto de .js. No es el caso de .mini-radio, que SI la reconstruye
   rdRenderList() y por eso sigue viva mas abajo. */

.imp-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  margin: 18px 0;
}
.imp-word {
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: 1px;
  padding: 14px 22px;
  border-radius: 14px;
  cursor: pointer;
  color: var(--text);
  background: var(--tile);
  border: 1px solid var(--panel-border);
  box-shadow: 0 0 12px rgba(155, 48, 255, 0.25);
  transition: opacity 0.35s, transform 0.15s, background 0.2s, border-color 0.2s;
}
.imp-word:hover { transform: translateY(-2px); background: rgba(155, 48, 255, 0.28); }
.imp-word.removed {
  opacity: 0.25;
  text-decoration: line-through;
  background: rgba(83, 141, 78, 0.25);
  border-color: var(--tile-green);
  pointer-events: none;
  transform: none;
}
.imp-word.impostor-reveal {
  background: rgba(141, 48, 48, 0.5);
  border-color: #e0442e;
  box-shadow: 0 0 14px rgba(224, 68, 46, 0.5);
  pointer-events: none;
}
.imp-word.shake { animation: impShake 0.4s; }
@keyframes impShake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

/* amber hint text */
.imp-hint {
  text-align: center;
  font-weight: 700;
  color: #f0d47a;
  background: rgba(245, 197, 24, 0.1);
  border: 1px dashed rgba(245, 197, 24, 0.55);
  border-radius: 10px;
  padding: 9px 14px;
  margin-bottom: 6px;
  animation: chipIn 0.35s ease;
}
.imp-hint.hidden { display: none; }

@media (max-width: 480px) {
  .imp-word { font-size: 0.9rem; padding: 11px 15px; }
}

/* ================= CONNECTIONS ================= */
/* fixed colors by category ORDER in the JSON (index 0-3), same in every puzzle */
/* 🚨 ESTE :root NO SE BORRA AUNQUE ESTE BAJO EL TITULAR DE CONNECTIONS Y EL
   DIBUJO DE SU TARJETA YA NO EXISTA. Los cuatro tokens los lee `.conn-group.g0`
   a `.g3`, mas abajo, que es lo que pinta cada categoria RESUELTA en el juego
   de verdad — app.js (~2384) monta `conn-group g${catIndex}`.
   Hasta el 2026-09-12 tambien los leia `.mini-conn.c0-.c3`, el dibujo de la
   tarjeta, y esa parte si se fue. Llevarse el :root con ella dejaria las cuatro
   categorias del juego SIN COLOR, y sin ningun error que lo avise. */
:root {
  --conn-0: #7c3aed; /* violet */
  --conn-1: #2f5fa8; /* blue */
  --conn-2: #b0448a; /* pink */
  --conn-3: #b8862e; /* warm amber */
}

/* 🚨 AQUI VIVIA el dibujo de portada de CONNECTIONS (.mini-connections y
   .mini-conn con sus .c0-.c3). Se retiro el 2026-09-12, cuando la tarjeta paso
   a ilustracion (CONNECTIONS.png). 0 apariciones en cualquier .js. */

.conn-wrap {
  max-width: 640px;
  margin: 20px auto;
  padding: 26px 20px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
}
.conn-wrap.hidden { display: none; }

/* solved group cards: full-width, category name on top, 4 words below */
.conn-solved { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }
.conn-group {
  border-radius: 12px;
  padding: 12px 14px;
  text-align: center;
  color: #fff;
  animation: chipIn 0.35s ease;
}
.conn-group.g0 { background: var(--conn-0); }
.conn-group.g1 { background: var(--conn-1); }
.conn-group.g2 { background: var(--conn-2); }
.conn-group.g3 { background: var(--conn-3); }
.conn-group-title { font-weight: 900; letter-spacing: 1px; text-transform: uppercase; font-size: 0.9rem; margin-bottom: 4px; }
.conn-group-words { font-weight: 600; font-size: 0.95rem; opacity: 0.95; }

/* 4x4 word grid */
.conn-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin: 10px 0 14px;
}
.conn-word {
  min-height: 56px;
  padding: 8px 4px;
  border-radius: 12px;
  border: 1px solid var(--line-soft);
  background: var(--tile);
  color: var(--text);
  font-weight: 800;
  font-size: 0.9rem;
  letter-spacing: 0.5px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.1s, box-shadow 0.15s;
  overflow-wrap: anywhere;
}
.conn-word:hover { background: rgba(155, 48, 255, 0.25); }
.conn-word.selected {
  background: rgba(155, 48, 255, 0.4);
  border: 2px solid var(--accent-bright);
  box-shadow: 0 0 12px rgba(185, 92, 255, 0.6);
  transform: scale(1.04);
}

#conn-message:not(:empty) { animation: chipIn 0.3s ease; }

.conn-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}
#conn-submit:disabled { opacity: 0.4; cursor: default; }

.conn-end { max-width: 560px; }
.conn-end .conn-solved { margin: 0 0 20px; text-align: center; }

@media (max-width: 480px) {
  .conn-grid { grid-template-columns: repeat(3, 1fr); gap: 7px; }
  .conn-word { font-size: 0.75rem; min-height: 48px; }
}

/* ================= IS IT A REAL WORD? ================= */
/* 🚨 AQUI VIVIA el dibujo de portada de IS IT A REAL WORD? (.mini-realword y
   sus cinco .mini-rw-*). Se retiro el 2026-09-12, cuando la tarjeta paso a
   ilustracion (REALWORD.png). Comprobado clase por clase antes de borrar: 0
   apariciones en app.js y en el resto de .js. */

.rw-wrap {
  max-width: 520px;
  margin: 24px auto;
  padding: 34px 24px 40px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
  text-align: center;
}
.rw-wrap.hidden { display: none; }

/* 6-second timer bar */
.rw-timer {
  height: 10px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.1);
  overflow: hidden;
  margin: 14px 0 4px;
}
.rw-timer-fill {
  height: 100%;
  width: 100%;
  border-radius: 5px;
  background: linear-gradient(90deg, var(--accent), var(--accent-bright));
}
.rw-timer-fill.urgent {
  background: #e0442e;
  animation: rwPulse 0.5s infinite alternate;
}
@keyframes rwPulse {
  from { opacity: 1; }
  to { opacity: 0.55; }
}
.rw-seconds { font-weight: 800; color: var(--text-dim); font-size: 0.9rem; min-height: 20px; }
.rw-seconds.urgent { color: #ff8a8a; }

.rw-question { color: var(--text-dim); font-size: 1.05rem; margin: 26px 0 10px; }
.rw-word {
  font-size: 2.6rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #fff;
  margin-bottom: 8px;
  min-height: 60px;
}

/* brief ✓ / ✗ feedback between words */
.rw-feedback { font-size: 2rem; font-weight: 900; min-height: 44px; }
.rw-feedback.ok { color: #7ee2a0; animation: chipIn 0.2s ease; }
.rw-feedback.bad { color: #ff6b6b; animation: chipIn 0.2s ease; }

.rw-buttons { display: flex; gap: 16px; justify-content: center; margin-top: 10px; }
.rw-btn {
  flex: 1;
  max-width: 190px;
  padding: 18px 10px;
  border-radius: 14px;
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: 1px;
  cursor: pointer;
  transition: filter 0.1s, transform 0.1s;
}
.rw-btn:hover { filter: brightness(1.2); }
.rw-btn:active { transform: scale(0.96); }
.rw-btn:disabled { opacity: 0.45; cursor: default; }
.rw-yes { background: rgba(83, 141, 78, 0.35); border: 2px solid var(--tile-green); color: #b8e6b3; }
.rw-no { background: rgba(141, 48, 48, 0.35); border: 2px solid #e0442e; color: #ffb3b3; }

/* end-of-game word summary */
.rw-summary li.was-missed { background: rgba(141, 48, 48, 0.25); }
.rw-summary .rw-tag { font-size: 0.8rem; opacity: 0.8; margin-left: 6px; }

@media (max-width: 480px) {
  .rw-word { font-size: 2rem; }
  .rw-btn { padding: 16px 8px; font-size: 1.05rem; }
}

/* ================= BOMB WORD ================= */
.mini-bomb { text-align: center; }
.mini-bomb-icon { font-size: 2rem; margin-bottom: 6px; }
.mini-bomb-prefix { font-size: 1.8rem; font-weight: 900; letter-spacing: 3px; color: #fff; margin-bottom: 8px; }
.mini-bomb-cursor { color: var(--accent-bright); animation: rwPulse 0.7s infinite alternate; }

.bw-wrap { max-width: 560px; }
.bw-status { display: flex; justify-content: space-between; margin-bottom: 4px; }

.bw-prefix {
  font-size: 3.2rem;
  font-weight: 900;
  letter-spacing: 6px;
  color: #fff;
  margin: 6px 0 10px;
  min-height: 76px;
}
.bw-bomb {
  display: inline-block;
  font-size: 2.4rem;
  letter-spacing: 0;
  margin-right: 6px;
}
.bw-bomb.urgent { animation: bwShake 0.25s infinite; }
@keyframes bwShake {
  0%, 100% { transform: rotate(0) scale(1); }
  25% { transform: rotate(-8deg) scale(1.08); }
  75% { transform: rotate(8deg) scale(1.08); }
}

.bw-form { max-width: 400px; margin: 6px auto 0; }
.bw-form input { text-transform: uppercase; letter-spacing: 2px; font-weight: 700; }

/* lives: purple hearts that dim as you lose them */
.bw-lives { font-size: 1.1rem; letter-spacing: 3px; }
.bw-lives.hidden { display: none; }
.bw-heart { transition: opacity 0.3s, filter 0.3s; }
.bw-heart.lost { opacity: 0.18; filter: grayscale(1); }

/* "LEVEL X" splash between levels: holds, then fades out via CSS */
.bw-transition {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 2, 18, 0.92);
  opacity: 1;
  transition: opacity 0.4s ease;
}
.bw-transition.fade { opacity: 0; }
.bw-transition.hidden { display: none; }
.bw-transition-text {
  font-size: clamp(2.5rem, 10vw, 4.5rem);
  font-weight: 900;
  letter-spacing: 8px;
  color: var(--accent-bright);
  text-shadow: 0 0 30px rgba(155, 48, 255, 0.8);
  animation: chipIn 0.3s ease;
}

@media (max-width: 480px) {
  .bw-prefix { font-size: 2.3rem; letter-spacing: 4px; }
}

/* ================= WAFFLE ================= */
.wf-wrap {
  max-width: 560px;
  margin: 20px auto;
  padding: 26px 20px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
  text-align: center;
}
.wf-wrap.hidden { display: none; }

.wf-swaps {
  font-weight: 900;
  letter-spacing: 2px;
  font-size: 1.05rem;
  color: var(--accent-bright);
  margin-bottom: 16px;
}
.wf-swaps.low { color: #ff8a8a; animation: rwPulse 0.6s infinite alternate; }

/* the waffle grid: N columns set inline from JS */
.wf-grid {
  display: grid;
  gap: 6px;
  justify-content: center;
  margin-bottom: 12px;
}
.wf-cell {
  width: var(--wf-cell, 56px);
  height: var(--wf-cell, 56px);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 900;
  font-size: calc(var(--wf-cell, 56px) * 0.45);
  color: #fff;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  transition: transform 0.12s, box-shadow 0.12s, background 0.25s;
  padding: 0;
}
.wf-cell.green { background: var(--tile-green); cursor: default; }
.wf-cell.yellow { background: var(--tile-yellow); }
.wf-cell.gray { background: var(--tile-gray); }
.wf-cell.yellow:hover, .wf-cell.gray:hover { transform: scale(1.06); }
.wf-cell.hole { background: transparent; pointer-events: none; }
.wf-cell.selected {
  outline: 3px solid var(--accent-bright);
  box-shadow: 0 0 16px rgba(185, 92, 255, 0.8);
  transform: scale(1.08);
  z-index: 2;
}

.intro-extra {
  color: var(--accent-bright);
  font-weight: 700;
  font-size: 0.9rem;
  margin-bottom: 18px;
  min-height: 20px;
}
.intro-extra:empty { display: none; }

@media (max-width: 480px) {
  .wf-wrap { padding: 20px 8px; }
}

/* ================= EMOJI BOMB ================= */
.mini-eb-emojis { font-size: 1.4rem; letter-spacing: 4px; margin-bottom: 8px; }

/* the three emoji clues: big, centered, never split (each in its own span) */
.eb-emojis {
  font-size: 3rem;
  display: flex;
  gap: 22px;
  justify-content: center;
  margin: 4px 0 10px;
  min-height: 64px;
}

/* Hardcore mode: subtle red/orange danger accent over the same dark palette */
.rw-wrap.eb-hardcore {
  border-color: #b33a2a;
  box-shadow: 0 0 24px rgba(224, 68, 46, 0.25);
}
.eb-hardcore .rw-timer-fill { background: linear-gradient(90deg, #e0442e, #f5851f); }
.eb-hardcore .bw-prefix { text-shadow: 0 0 20px rgba(224, 68, 46, 0.6); }
.eb-lives-hardcore {
  font-weight: 900;
  font-size: 0.85rem;
  letter-spacing: 1px;
  color: #ff8a6b;
  border: 1px solid rgba(224, 68, 46, 0.6);
  border-radius: 10px;
  padding: 3px 10px;
}

.eb-end-prompt {
  font-size: 1.5rem;
  margin: -6px 0 20px;
  color: var(--text-dim);
}
.eb-end-prompt strong { color: #fff; letter-spacing: 1px; }

@media (max-width: 480px) {
  .eb-emojis { font-size: 2.3rem; gap: 14px; }
}

/* ================= STRANDS ================= */
/* state colors: normal, active selection, solved theme, spangram, hint */
:root {
  --st-theme-color: #58b8d8;   /* light cyan-blue for solved theme words */
  --st-span-color: #e8c34a;    /* gold for the spangram */
  --st-hint-color: #ff7ad9;    /* pink ring for hint letters */
}

.st-wrap {
  max-width: 480px;
  margin: 18px auto;
  padding: 22px 16px 26px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
  text-align: center;
}
.st-wrap.hidden { display: none; }

.st-theme-label { font-size: 0.7rem; letter-spacing: 3px; color: var(--text-dim); margin-bottom: 2px; }
.st-theme {
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--accent-bright);
  margin-bottom: 8px;
}

/* the word being traced right now */
.st-current {
  min-height: 28px;
  font-weight: 900;
  font-size: 1.15rem;
  letter-spacing: 3px;
  color: #fff;
}

.st-board {
  position: relative;
  display: inline-block;
  touch-action: none; /* the drag gesture owns the touch — no page scroll */
  user-select: none;
  -webkit-user-select: none;
  margin-bottom: 6px;
}
.st-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}
.st-line { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.st-line.active { stroke: rgba(168, 85, 247, 0.55); stroke-width: 14; }
.st-line.theme { stroke: rgba(88, 184, 216, 0.4); stroke-width: 14; }
.st-line.span { stroke: rgba(232, 195, 74, 0.4); stroke-width: 14; }

.st-grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(6, var(--st-cell, 52px));
  gap: 6px;
}
.st-cell {
  width: var(--st-cell, 52px);
  height: var(--st-cell, 52px);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: calc(var(--st-cell, 52px) * 0.42);
  color: var(--text);
  border-radius: 50%;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, transform 0.12s ease, box-shadow 0.18s ease;
}
.st-cell.sel {
  background: var(--accent);
  color: #fff;
  transform: scale(1.08);
}
/* solved cells keep their color but stay traceable (letters can be reused) */
.st-cell.solved-theme { background: var(--st-theme-color); color: #0b2530; }
.st-cell.solved-span { background: var(--st-span-color); color: #2e2405; }
/* an active trace must visually win over the solved color */
.st-grid .st-cell.sel { background: var(--accent); color: #fff; transform: scale(1.08); }
.st-cell.hinted { box-shadow: 0 0 0 3px var(--st-hint-color), 0 0 14px rgba(255, 122, 217, 0.6); }
/* sequential hint reveal: each letter grows briefly, then settles — the wave
   itself communicates the order (first letter pops first, and so on) */
.st-cell.hint-pop {
  transform: scale(1.32);
  transition: transform 0.22s ease-out;
  z-index: 3;
}
.st-cell.pulse { animation: stPulse 0.45s ease; }
@keyframes stPulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.22); background: rgba(83, 141, 78, 0.6); }
  100% { transform: scale(1); }
}
.st-cell.bad { animation: stBad 0.4s ease; }
@keyframes stBad {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); background: rgba(224, 68, 46, 0.5); }
  75% { transform: translateX(5px); background: rgba(224, 68, 46, 0.5); }
}

/* SPANGRAM! flash over the board */
.st-flash {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.2rem;
  font-weight: 900;
  letter-spacing: 4px;
  color: var(--st-span-color);
  text-shadow: 0 0 24px rgba(232, 195, 74, 0.9);
  pointer-events: none;
  animation: chipIn 0.3s ease;
}
.st-flash.hidden { display: none; }

.st-status { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 4px 12px; margin: 8px 4px 12px; }

/* final-screen word list: found vs missed, spangram highlighted */
.st-wordlist {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
  margin: 0 0 20px;
}
/* the <li> is now just a wrapper — the chip styling lives on .st-word-chip,
   which is a <button> for words the player found and a <span> for missed ones */
.st-wordlist li { display: flex; }
.st-word-chip {
  font-size: 0.82rem;
  font-weight: 700;
  font-family: inherit;
  padding: 5px 10px;
  border: none;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-dim);
}
.st-wordlist li.found .st-word-chip { background: rgba(88, 184, 216, 0.28); color: #cdeef7; }
.st-wordlist li.missed .st-word-chip { color: #d99a9a; text-decoration: line-through; opacity: 0.75; }
.st-wordlist li.span .st-word-chip { background: rgba(232, 195, 74, 0.28); color: #f4e3a0; }
.st-wordlist li.span.missed .st-word-chip { color: #f4e3a0; opacity: 0.6; }
.st-wordlist .span-tag { font-size: 0.62rem; letter-spacing: 1px; opacity: 0.85; margin-left: 4px; }

/* found words are tappable: subtle dotted underline hints it without adding
   visual noise, and the chip lifts + brightens on hover/focus. The selected
   chip stays highlighted so it's clear which meaning is on screen. */
.st-word-btn {
  cursor: pointer;
  text-decoration: underline dotted rgba(205, 238, 247, 0.5);
  text-underline-offset: 3px;
  transition: background 0.15s, transform 0.15s, box-shadow 0.15s;
}
.st-word-btn:hover,
.st-word-btn:focus-visible {
  background: rgba(88, 184, 216, 0.45);
  color: #eafaff;
  transform: translateY(-1px);
}
.st-word-btn:focus-visible { outline: 2px solid var(--accent-bright, #d846ef); outline-offset: 2px; }
.st-wordlist li.span .st-word-btn { text-decoration-color: rgba(244, 227, 160, 0.55); }
.st-wordlist li.span .st-word-btn:hover,
.st-wordlist li.span .st-word-btn:focus-visible { background: rgba(232, 195, 74, 0.45); color: #fff6d4; }
.st-word-btn.active {
  background: rgba(88, 184, 216, 0.5);
  color: #fff;
  box-shadow: 0 0 0 1px rgba(205, 238, 247, 0.6);
  text-decoration: none;
}
.st-wordlist li.span .st-word-btn.active {
  background: rgba(232, 195, 74, 0.5);
  box-shadow: 0 0 0 1px rgba(244, 227, 160, 0.6);
}

.st-wordlist-hint {
  margin: -12px 0 12px;
  font-size: 0.72rem;
  color: var(--text-dim);
  opacity: 0.75;
  text-align: center;
}

/* shared meaning panel below the chips — collapsed until a word is tapped */
.st-word-meaning {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  margin: 0 0 16px;
  padding: 0 12px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.28);
  text-align: left;
  transition: max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease;
}
.st-word-meaning.visible { max-height: 220px; opacity: 1; padding: 10px 12px; }
.st-meaning-word {
  display: block;
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #cdeef7;
  margin-bottom: 3px;
}
.st-meaning-def { margin: 0; font-size: 0.85rem; line-height: 1.4; color: var(--text); }

@media (max-width: 480px) {
  .st-grid { grid-template-columns: repeat(6, var(--st-cell, 13vw)); }
  .st-cell { width: var(--st-cell, 13vw); height: var(--st-cell, 13vw); }
  /* roomier tap area for the clickable found-words on touch screens (kept well
     under 44px on purpose — these chips wrap in a row and full-size targets
     would push the word list off the end panel) */
  .st-word-chip { padding: 9px 12px; }
}

/* ================= MOBILE ================= */
@media (max-width: 480px) {
  /* card grid: single column. The cap must match .game-card's own width below
     or the card floats off-centre inside a wider container. */
  .card-grid { max-width: 192px; } /* 160 + 32 */
  .game-card { width: 160px; }
  /* Solo el padding. El alto lo manda el `aspect-ratio: 1/1` de la regla base.
     🚨 Aqui habia un `height: auto; min-height: 150px`. El `height: auto` es
     redundante (ya lo es arriba) y el `min-height` se QUITO: con la tarjeta a
     160px un minimo de 150 de alto no molestaba hoy, pero es un numero suelto
     que contradice al `aspect-ratio` en cuanto alguien toque el ancho — y
     ganaria el min-height, dejando la caja no cuadrada sin que nadie sepa por
     que. */
  .card-art { padding: 16px 10px; }

  /* 🚨 AQUI HABIA DOS OVERRIDES DE --art-scale, de Word Game y de Word Lab
     Radio, y se BORRARON el 2026-09-11. No sobraban por limpieza: sus propios
     comentarios decian que existian porque «la caja movil (158x150) es casi
     cuadrada y la de escritorio (228x190) no», o sea que object-fit ajustaba
     por ejes distintos en cada breakpoint y hacia falta una escala por cada
     uno. Con la portada cuadrada LAS DOS CAJAS SON CUADRADAS y el mismo
     --art-scale vale en las dos. Dejarlos habria dejado esas dos tarjetas con
     una escala pensada para una geometria que ya no existe.
     Si alguna necesita algun dia un valor propio de movil, se mide y se
     escribe un comentario nuevo: no se resucitan estos. */

  /* FILL IN THE BLANKS only: this preview is CSS-drawn (.fib-logo), not an
     image, so the --art-scale knob does not reach it. Its wordmark is a fixed
     165px wide (1.5rem + 4px letter-spacing), which overflows the 158px mobile
     card and gets clipped by .card-art's overflow:hidden — ~3px off each end
     of FILL IN / BLANKS. Scaling the whole lockup keeps the proportions and
     leaves a margin comparable to the other cards. Desktop is untouched: from
     481px up the card is 228px wide and the logo already fits. */
  .fib-logo { transform: scale(0.85); }

  .tile { width: 44px; height: 44px; font-size: 1.4rem; }
  .key { height: 48px; font-size: 0.85rem; }

  /* PLAY button: guaranteed 44px tap target, same width on every card
     since .card-play is already block-level 100% of its .card-footer */
  .card-play { min-height: 44px; display: flex; align-items: center; justify-content: center; padding: 8px 0; }

  /* ---- IMPOSTOR home preview ---- */
  .mini-impo { gap: 8px; }
  .mini-impo-spy { width: clamp(40px, 14vw, 56px); height: clamp(40px, 14vw, 56px); }
  .mini-impo-word { font-size: clamp(0.75rem, 4vw, 0.95rem); letter-spacing: 2px; }
  .mini-impo-pills { gap: 5px 6px; }
  .mini-impo-pills .mini-chip { font-size: 0.6rem; padding: 4px 8px; }

  /* ---- WORD LINKS home preview ---- */

  /* ---- SPOT THE ERROR home preview ---- */

}

/* =====================================================
   STAR PARTY
   ===================================================== */
:root { --sp-accent: #a855f7; --sp-good: #22c55e; --sp-bad: #ef4444; --sp-neutral: #eab308; }

/* ---- setup screen ---- */
.sp-setup {
  max-width: 520px;
  margin: 10px auto 40px;
  padding: 26px 22px 30px;
  text-align: center;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 22px;
  box-shadow: 0 0 40px rgba(168, 85, 247, 0.28);
}
.sp-setup.hidden { display: none; }
.sp-title { font-size: 2.1rem; letter-spacing: 2px; font-weight: 900; }
.sp-title-star { color: var(--sp-neutral); filter: drop-shadow(0 0 8px var(--sp-neutral)); }
.sp-sub { color: var(--text-dim); margin: 10px 0 22px; line-height: 1.5; font-size: 0.95rem; }
.sp-label { color: var(--text-dim); font-size: 0.9rem; margin-bottom: 10px; letter-spacing: 1px; text-transform: uppercase; }
.sp-count-btns { display: flex; justify-content: center; gap: 10px; margin-bottom: 22px; }
.sp-count-btn {
  width: 46px; height: 46px; border-radius: 12px;
  border: 1px solid rgba(168, 85, 247, 0.5);
  background: #2a1140; color: var(--text);
  font-weight: 800; font-size: 1.05rem; cursor: pointer;
  transition: transform 0.1s, background 0.15s, box-shadow 0.15s;
}
.sp-count-btn:hover { background: #3a1a55; }
.sp-count-btn.active {
  background: var(--sp-accent); color: #fff;
  box-shadow: 0 0 16px rgba(168, 85, 247, 0.7);
  transform: translateY(-2px);
}
.sp-player-rows { display: flex; flex-direction: column; gap: 10px; margin-bottom: 24px; }
.sp-player-row { display: flex; align-items: center; gap: 10px; }
.sp-seat-dot { width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 8px currentColor; }
.sp-name-input {
  flex: 1; min-width: 0;
  background: #1c0a2b; color: var(--text);
  border: 1px solid rgba(168, 85, 247, 0.4);
  border-radius: 8px; padding: 9px 12px; font-size: 0.95rem;
}
.sp-name-input:focus { outline: none; border-color: var(--sp-accent); }
.sp-type-toggle {
  flex: 0 0 auto; width: 108px;
  border: none; border-radius: 8px; padding: 9px 8px;
  font-weight: 700; font-size: 0.85rem; cursor: pointer;
  transition: filter 0.1s;
}
.sp-type-toggle:hover { filter: brightness(1.12); }
.sp-type-toggle.is-human { background: rgba(56, 189, 248, 0.25); color: #bfe9ff; border: 1px solid rgba(56,189,248,0.5); }
.sp-type-toggle.is-cpu { background: rgba(168, 85, 247, 0.25); color: #e6ccff; border: 1px solid rgba(168,85,247,0.5); }

/* ---- table / felt ---- */
.sp-table.hidden { display: none; }
.sp-hud { text-align: center; margin: 4px 0 6px; }
.sp-round {
  display: inline-block;
  background: rgba(0,0,0,0.4);
  border: 1px solid rgba(168, 85, 247, 0.5);
  color: var(--text); font-weight: 800; letter-spacing: 2px;
  padding: 6px 18px; border-radius: 999px; font-size: 0.9rem;
}
.sp-felt {
  position: relative;
  width: min(96vw, 740px);
  height: min(96vw, 740px);
  margin: 6px auto 30px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 42%, rgba(168, 85, 247, 0.28) 0%, rgba(42, 10, 61, 0.55) 45%, rgba(12, 2, 20, 0.7) 72%),
    #180627;
  border: 3px solid rgba(168, 85, 247, 0.45);
  box-shadow: 0 0 60px rgba(168, 85, 247, 0.35), inset 0 0 80px rgba(0,0,0,0.6);
}

/* seats around the ellipse */
.sp-seats { position: absolute; inset: 0; }
.sp-seat {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 158px;
  text-align: center;
  transition: transform 0.2s;
}
.sp-seat-inner {
  background: rgba(15, 4, 26, 0.85);
  border: 2px solid rgba(255,255,255,0.12);
  border-radius: 16px;
  padding: 12px 12px 13px;
  box-shadow: 0 4px 14px rgba(0,0,0,0.5);
  transition: border-color 0.2s, box-shadow 0.2s;
}
.sp-seat.current { z-index: 3; }
.sp-seat.current .sp-seat-inner {
  border-color: var(--seat-color);
  box-shadow: 0 0 22px var(--seat-color), 0 0 40px var(--seat-color);
  animation: sp-seat-pulse 1.4s ease-in-out infinite;
}
@keyframes sp-seat-pulse {
  0%, 100% { box-shadow: 0 0 16px var(--seat-color), 0 0 30px rgba(255,255,255,0.08); }
  50% { box-shadow: 0 0 26px var(--seat-color), 0 0 48px var(--seat-color); }
}
.sp-seat-name {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  font-weight: 800; font-size: 1.02rem; margin-bottom: 7px;
  color: #fff; white-space: nowrap; overflow: hidden;
}
.sp-seat-label { overflow: hidden; text-overflow: ellipsis; }
.sp-seat-badge { flex: 0 0 auto; font-size: 1rem; }
.sp-defer { display: none; flex: 0 0 auto; font-size: 1rem; filter: drop-shadow(0 0 4px #fff); }
.sp-defer.show { display: inline; }
.sp-star-bar {
  margin-top: 7px; height: 8px; border-radius: 999px;
  background: rgba(255,255,255,0.1); overflow: hidden;
}
.sp-star-fill {
  height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--seat-color), #fff);
  transition: width 0.5s ease;
}
.sp-star-fill.neg { background: linear-gradient(90deg, #7a1515, var(--sp-bad)); }

/* center stage: a floating panel where the action happens */
.sp-center {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: min(58%, 340px);
  z-index: 4;
  text-align: center;
}
/* the wheel is the protagonist — give it a much wider stage */
.sp-center.sp-mode-wheel { width: min(86%, 480px); }
.sp-center > * {
  background: rgba(18, 5, 30, 0.94);
  border: 2px solid rgba(168, 85, 247, 0.55);
  border-radius: 18px;
  padding: 16px 16px 18px;
  box-shadow: 0 0 34px rgba(168, 85, 247, 0.4);
}
.sp-center-title { font-weight: 800; font-size: 1rem; margin-bottom: 8px; }
.sp-center-hint { color: var(--text-dim); font-size: 0.8rem; margin-bottom: 12px; }

/* turn splash */
.sp-transition .sp-trans-sub { color: var(--text-dim); font-size: 0.8rem; letter-spacing: 1px; }
.sp-transition .sp-trans-name { font-size: 1.7rem; font-weight: 900; margin: 4px 0; text-shadow: 0 0 16px currentColor; }
.sp-transition .sp-trans-tag { color: var(--text-dim); font-size: 0.82rem; text-transform: uppercase; letter-spacing: 2px; }
.sp-transition { animation: sp-pop 0.3s ease; }
@keyframes sp-pop { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* risk buttons — reference redesign: dark box + colored stroke + star icons,
   with the difficulty label sitting BELOW the box, outside its border */
.sp-risk-btns { display: flex; justify-content: center; gap: 16px; }
.sp-risk-btn {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: none; border: none; padding: 0; cursor: pointer;
  transition: transform 0.1s;
}
.sp-risk-btn:hover { transform: translateY(-3px); }
.sp-risk-btn.picked .sp-risk-box { box-shadow: 0 0 22px #fff, 0 0 10px currentColor inset; transform: scale(1.06); }
.sp-risk-btn.dim { opacity: 0.35; }
.sp-risk-box {
  position: relative;
  width: 92px; height: 92px;
  background: rgba(8, 2, 14, 0.85);
  border: 3px solid; border-radius: 16px;
  transition: box-shadow 0.15s, transform 0.15s;
}
.sp-risk-btn:hover .sp-risk-box { box-shadow: 0 0 18px currentColor; }
/* NOTE: risk-1 (Basic) uses --sp-correct (green), NOT --sp-good — --sp-good is the
   wildcard GOOD category color and was recolored blue elsewhere in the game. */
.sp-risk-btn.risk-1 .sp-risk-box { border-color: var(--sp-correct); color: var(--sp-correct); }
.sp-risk-btn.risk-3 .sp-risk-box { border-color: var(--sp-neutral); color: var(--sp-neutral); }
.sp-risk-btn.risk-5 .sp-risk-box { border-color: var(--sp-bad); color: var(--sp-bad); }
.sp-bet-stars { position: absolute; inset: 0; }
.sp-bet-star { position: absolute; line-height: 0; }
/* THE single shared star icon glow (on the <svg class="sp-star-ico"> itself) —
   every star anywhere in the game (bet boxes, floating over seats, standings,
   flash messages, flying tokens, wheel hub, end screen…) gets this SAME glow,
   kept EXACTLY as it was on the reference bet-box design (don't touch the values). */
.sp-star-ico {
  display: inline-block; vertical-align: -0.15em;
  filter: drop-shadow(0 0 6px rgba(255, 182, 224, 0.95)) drop-shadow(0 2px 2px rgba(0, 0, 0, 0.5));
}
.sp-risk-label { font-weight: 800; font-size: 0.95rem; color: #fff; }
/* "pos/neg" here mean gain/loss (sign of a number), NOT the wildcard GOOD category
   — must use --sp-correct (green), not --sp-good (blue), same fix as .sp-float.pos */
.sp-q-tier .pos { color: var(--sp-correct, #22c55e); }
.sp-q-tier .neg { color: var(--sp-bad); }
.sp-q-tier .zero { color: var(--text-dim); }

/* question */
.sp-q-tier {
  display: inline-block; font-weight: 800; font-size: 0.72rem; letter-spacing: 1px;
  text-transform: uppercase; padding: 3px 10px; border-radius: 999px; margin-bottom: 8px;
  border: 1px solid;
}
.sp-q-tier.risk-1 { color: var(--sp-correct, #22c55e); border-color: var(--sp-correct, #22c55e); }
.sp-q-tier.risk-3 { color: var(--sp-neutral); border-color: var(--sp-neutral); }
.sp-q-tier.risk-5 { color: var(--sp-bad); border-color: var(--sp-bad); }
.sp-q-text { font-size: 1.35rem; font-weight: 700; line-height: 1.35; margin-bottom: 16px; }
.sp-q-opts { display: grid; gap: 10px; }
.sp-q-opt {
  background: #241036; color: var(--text);
  border: 1px solid rgba(168, 85, 247, 0.4);
  border-radius: 11px; padding: 14px 14px; font-size: 1.12rem; font-weight: 700;
  cursor: pointer; transition: background 0.12s, transform 0.1s;
}
.sp-q-opt:hover:not(:disabled) { background: #33184d; transform: translateY(-1px); }
.sp-q-opt:disabled { cursor: default; }
.sp-q-opt.correct { background: rgba(34, 197, 94, 0.32); border-color: var(--sp-correct, #22c55e); color: #d6ffe4; }
.sp-q-opt.wrong { background: rgba(239, 68, 68, 0.32); border-color: var(--sp-bad); color: #ffd8d8; }
.sp-q-feedback { margin-top: 12px; font-weight: 800; font-size: 1rem; }
.sp-q-feedback small { display: block; font-size: 0.7rem; font-weight: 600; opacity: 0.8; margin-top: 2px; }
.sp-q-feedback.gain { color: var(--sp-correct, #22c55e); }
.sp-q-feedback.loss { color: var(--sp-bad); }

/* wheel */
.sp-wheel-stage { display: flex; flex-direction: column; align-items: center; }
.sp-wheel-wrap { position: relative; width: min(72vw, 400px); height: min(72vw, 400px); margin: 10px 0 16px; }
.sp-wheel {
  position: absolute; inset: 0; border-radius: 50%;
  border: 6px solid #12041e;
  box-shadow:
    0 0 0 4px rgba(168, 85, 247, 0.8),
    0 0 28px 6px rgba(168, 85, 247, 0.75),
    inset 0 0 26px rgba(0,0,0,0.6);
  will-change: transform;
}
.sp-wheel-hub {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 52px; height: 52px; border-radius: 50%; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 40% 35%, #fff, var(--sp-accent) 70%);
  color: #fff; font-size: 1.2rem;
  box-shadow: 0 0 20px 4px rgba(255,255,255,0.9), 0 0 34px var(--sp-accent);
}
.sp-wheel-pointer {
  position: absolute; top: -18px; left: 50%; transform: translateX(-50%);
  width: 0; height: 0; z-index: 3;
  border-left: 16px solid transparent; border-right: 16px solid transparent;
  border-top: 28px solid #fff;
  filter: drop-shadow(0 0 8px rgba(255,255,255,0.95));
}
.sp-spin-btn { margin-top: 4px; }
/* ARREGLO 5 — wheel pop-out: JS applies a back-ease cubic-bezier over this 0→1 grow */
@keyframes sp-pop-out { from { transform: scale(0.05); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* wildcard reveal card (after the wheel stops).
   Solid background is set HERE (not inherited from `.sp-center > *`) because this
   card is nested inside .sp-wheel-stage — a grandchild of .sp-center, not a direct
   child — so it was rendering fully transparent over the wheel underneath it. */
.sp-wc-card {
  background: rgba(14, 4, 24, 0.98);
  border-radius: 18px;
  padding: 16px 16px 18px;
  margin-top: 12px; border: 2px solid var(--wc-color) !important;
  box-shadow: 0 0 26px var(--wc-color) !important;
  animation: sp-pop 0.32s ease;
}
.sp-wc-cat { font-size: 0.66rem; letter-spacing: 2px; font-weight: 800; color: var(--wc-color); }
.sp-wc-name { font-size: 1.15rem; font-weight: 900; margin: 3px 0 5px; text-shadow: 0 0 12px var(--wc-color); }
.sp-wc-desc { font-size: 0.8rem; color: var(--text-dim); line-height: 1.4; }

/* steal picker */
.sp-steal-list { display: grid; gap: 8px; margin-top: 6px; }
.sp-steal-opt {
  display: flex; align-items: center; justify-content: space-between;
  background: #241036; color: var(--text);
  border: 2px solid var(--seat-color); border-radius: 10px;
  padding: 9px 12px; font-weight: 700; font-size: 0.9rem; cursor: pointer;
  transition: transform 0.1s, box-shadow 0.15s;
}
.sp-steal-opt:hover { transform: translateY(-2px); box-shadow: 0 0 16px var(--seat-color); }
.sp-steal-stars { color: var(--sp-neutral); font-weight: 800; }

/* flash toast over the felt */
.sp-flash {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%) translateY(-8px);
  z-index: 6; max-width: 84%;
  background: rgba(10, 2, 18, 0.92);
  border: 1px solid rgba(168, 85, 247, 0.7);
  border-radius: 999px; padding: 7px 16px;
  font-weight: 700; font-size: 0.82rem; text-align: center;
  opacity: 0; pointer-events: none; transition: opacity 0.25s, transform 0.25s;
  box-shadow: 0 0 20px rgba(168, 85, 247, 0.5);
}
.sp-flash.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* final standings */
.sp-standings { list-style: none; margin: 8px 0 20px; padding: 0; display: flex; flex-direction: column; gap: 7px; text-align: left; }
.sp-standing {
  display: flex; align-items: center; gap: 10px;
  background: #1c0a2b; border-left: 4px solid var(--seat-color);
  border-radius: 8px; padding: 9px 12px;
}
.sp-standing.winner { box-shadow: 0 0 16px var(--seat-color); background: #26113a; }
.sp-rank { font-weight: 900; color: var(--text-dim); width: 18px; }
.sp-standing-name { flex: 1; font-weight: 700; }
.sp-standing-stars { font-weight: 900; color: var(--sp-neutral); }
.sp-standing-stars.neg { color: var(--sp-bad); }

/* CPU "thinking" dots */
.sp-thinking { display: inline-flex; gap: 4px; margin-left: 4px; vertical-align: middle; }
.sp-thinking span {
  width: 7px; height: 7px; border-radius: 50%; background: var(--sp-accent);
  animation: sp-think 1s infinite ease-in-out;
}
.sp-thinking span:nth-child(2) { animation-delay: 0.16s; }
.sp-thinking span:nth-child(3) { animation-delay: 0.32s; }
@keyframes sp-think { 0%, 80%, 100% { opacity: 0.25; transform: scale(0.7); } 40% { opacity: 1; transform: scale(1); } }
.sp-q-thinking { margin-top: 10px; font-size: 0.82rem; color: var(--text-dim); font-weight: 700; }

/* AMBUSH alert at the start of a victim's turn */
.sp-ambush-alert { font-size: 1.15rem; font-weight: 800; animation: sp-pop 0.3s ease; }
.sp-ambush-alert .sp-ambush-amt { display: block; margin-top: 6px; font-size: 1.6rem; font-weight: 900; color: var(--sp-bad); text-shadow: 0 0 14px var(--sp-bad); }

/* EASY PICK banner + Cursed/High-Stakes tag on the question badge */
.sp-q-easy { font-size: 0.78rem; font-weight: 800; color: var(--sp-good); margin-bottom: 8px; }
.sp-q-mod { color: var(--sp-bad); font-weight: 800; }
.sp-q-tier b { margin: 0 2px; }

@media (max-width: 480px) {
  .sp-seat { width: 112px; }
  .sp-seat-inner { padding: 8px 7px 9px; }
  .sp-seat-name { font-size: 0.82rem; }
  .sp-seat-stars-num { font-size: 1.8rem; }
  .sp-center { width: min(64%, 300px); }
  .sp-center.sp-mode-wheel { width: min(92%, 360px); }
  .sp-q-text { font-size: 1.12rem; }
  .sp-q-opt { font-size: 1rem; padding: 11px; }
}

/* =====================================================
   STAR PARTY v4 — coin, dice, curse wheel
   ===================================================== */
/* reusable COIN */
.sp-coin-stage { display: flex; flex-direction: column; align-items: center; }
.sp-coin-wrap { perspective: 800px; margin: 12px 0; }
.sp-coin {
  position: relative; width: 130px; height: 130px; margin: 0 auto;
  transform-style: preserve-3d; transform: rotateY(0deg);
}
.sp-coin-face {
  position: absolute; inset: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 3.2rem;
  backface-visibility: hidden;
  border: 5px solid #ffe9a8;
  box-shadow: 0 0 26px var(--sp-accent), inset 0 0 20px rgba(0,0,0,0.35);
}
.sp-coin-heads { background: radial-gradient(circle at 38% 32%, #fff, var(--sp-accent) 78%); }
.sp-coin-tails { background: radial-gradient(circle at 38% 32%, #fff, #eab308 78%); transform: rotateY(180deg); }
.sp-coin-choice { display: flex; gap: 14px; justify-content: center; margin-top: 4px; }
.sp-coin-btn {
  padding: 12px 20px; border-radius: 12px; border: 2px solid var(--sp-accent);
  background: rgba(0,0,0,0.3); color: #fff; font-weight: 800; font-size: 1rem; cursor: pointer;
  transition: transform 0.1s, box-shadow 0.15s;
}
.sp-coin-btn.heads { border-color: #eab308; }
.sp-coin-btn:hover { transform: translateY(-2px); box-shadow: 0 0 16px currentColor; }
.sp-coin-btn.picked { box-shadow: 0 0 22px #fff; transform: scale(1.06); }
.sp-coin-btn.dim { opacity: 0.35; }
.sp-coin-caption { margin-top: 10px; font-weight: 700; font-size: 0.95rem; }
.sp-coin-caption b { color: var(--sp-neutral); }

/* reusable DICE */
.sp-dice-stage { display: flex; flex-direction: column; align-items: center; }
.sp-dice-wrap { margin: 14px 0; display: flex; justify-content: center; }
.sp-dice {
  width: 88px; height: 88px; border-radius: 16px;
  background: linear-gradient(150deg, #ffffff, #d8c6ff);
  box-shadow: 0 6px 18px rgba(0,0,0,0.55), inset 0 0 12px rgba(168,85,247,0.45);
  display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr);
  padding: 12px; gap: 2px;
}
.sp-pip { border-radius: 50%; }
.sp-pip.on { background: #3b0764; box-shadow: 0 0 4px rgba(0,0,0,0.4); }
.sp-dice-caption { font-weight: 700; font-size: 0.95rem; }
.sp-dice-caption b { color: var(--sp-neutral); font-size: 1.15rem; }

/* CURSE WHEEL number labels */
.sp-wheel-labels { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.sp-wheel-label {
  position: absolute; transform: translate(-50%, -50%);
  font-weight: 900; font-size: 1.05rem; color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.95), 0 0 6px rgba(0,0,0,0.7);
}

/* =====================================================
   STAR PARTY — UI/UX round (seat order, animations, pot, OK)
   ===================================================== */
/* big reveal card + OK button (ARREGLO 3 + 8) */
.sp-wc-big {
  animation: sp-pop-out 0.42s cubic-bezier(0.34, 1.56, 0.64, 1) both !important;
  min-width: 250px;
}
.sp-wc-big .sp-wc-cat { font-size: 0.8rem; }
.sp-wc-big .sp-wc-name { font-size: 1.7rem; }
.sp-wc-big .sp-wc-desc { font-size: 0.95rem; }
.sp-ok-btn { margin-top: 14px; }
.sp-pop-in { animation: sp-pop-out 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
/* wheel stage shrink/fade when dismissed to reveal the table (ARREGLO 4) */
.sp-dismiss { animation: sp-shrink 0.42s ease forwards; pointer-events: none; }
@keyframes sp-shrink { to { transform: scale(0.1); opacity: 0; } }

/* flying star token (transfers / pot) */
.sp-fly {
  position: absolute; z-index: 8; transform: translate(0, 0);
  display: flex; align-items: center; gap: 3px;
  font-weight: 900; pointer-events: none; will-change: transform;
  margin-left: -14px; margin-top: -14px;
}
.sp-fly-star { font-size: 1.5rem; color: var(--sp-neutral); filter: drop-shadow(0 0 6px var(--sp-neutral)); }
.sp-fly-num { font-size: 0.9rem; color: #fff; text-shadow: 0 1px 3px #000; }

/* floating +/- over a seat */
.sp-float {
  position: absolute; z-index: 9; transform: translate(-50%, -50%);
  font-weight: 900; font-size: 1.3rem; pointer-events: none;
  opacity: 0; transition: transform 1.3s ease-out, opacity 1.3s ease-out;
  text-shadow: 0 2px 5px rgba(0,0,0,0.8);
}
/* NOTE: green here uses --sp-correct, NOT --sp-good — --sp-good is the wildcard
   GOOD category color (blue) and must not leak into "positive amount" contexts. */
.sp-float.pos { color: var(--sp-correct, #22c55e); }
.sp-float.neg { color: var(--sp-bad); }
.sp-float.go { opacity: 1; transform: translate(-50%, -230%); }

/* central pot bubble */
.sp-pot {
  position: absolute; z-index: 7; transform: translate(-50%, -50%) scale(0.2);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 14px 20px; border-radius: 18px;
  background: rgba(18, 5, 30, 0.95); border: 2px solid var(--sp-neutral);
  box-shadow: 0 0 30px var(--sp-neutral); opacity: 0;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.35s;
}
.sp-pot.go { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.sp-pot-ico { font-size: 1.8rem; }
.sp-pot-num { font-weight: 900; font-size: 1.4rem; color: var(--sp-neutral); }

/* coin choice cards with real faces (ARREGLO 6) */
.sp-coin-btn {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 16px 24px;
}
.sp-coin-mini {
  width: 64px; height: 64px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 2rem;
  border: 4px solid #ffe9a8; box-shadow: 0 0 16px var(--sp-accent), inset 0 0 12px rgba(0,0,0,0.35);
}
.sp-coin-mini-heads { background: radial-gradient(circle at 38% 32%, #fff, var(--sp-accent) 78%); }
.sp-coin-mini-tails { background: radial-gradient(circle at 38% 32%, #fff, #eab308 78%); }
.sp-coin-btn-label { font-size: 0.95rem; letter-spacing: 1px; }

/* COIN DUEL: the two sides + their assigned faces (step 3) */
.sp-duel-faces { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 8px; }
.sp-duel-side { display: flex; flex-direction: column; align-items: center; gap: 4px; font-weight: 800; }
.sp-duel-name { font-size: 0.9rem; }
.sp-duel-vs { font-weight: 900; color: var(--text-dim); }

/* dice: give it perspective so the multi-axis tumble reads as 3D (B2) */
.sp-dice-wrap { perspective: 600px; }
.sp-dice { transform-style: preserve-3d; will-change: transform; }

/* =====================================================
   STAR PARTY — visual redesign round (standings, cards, risk)
   ===================================================== */
/* ---- C1: live standings panel (Mario-Party style) ----
   The panel is positioned ABSOLUTE so it never takes layout space from the
   table — .sp-table-main (hud + felt) centers on the full viewport exactly
   like it did before the panel existed (D2 fix: felt must stay centered). */
.sp-table-layout { position: relative; }
.sp-table-main { width: 100%; }
.sp-standings-panel {
  position: absolute;
  top: 4px; left: 14px;
  width: 200px;
  display: flex; flex-direction: column; gap: 6px;
  z-index: 2;
}
.sp-rank-row {
  position: relative;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px;
  border-radius: 10px;
  background: linear-gradient(90deg, var(--seat-color) 0%, color-mix(in srgb, var(--seat-color) 55%, #150522) 100%);
  box-shadow: 0 3px 8px rgba(0,0,0,0.4);
  color: #fff;
}
.sp-rank-row.sp-rank-first {
  box-shadow: 0 0 0 2px #ffd75e, 0 0 16px rgba(255, 215, 94, 0.65), 0 3px 8px rgba(0,0,0,0.4);
}
.sp-rank-name {
  flex: 1 1 auto; min-width: 0; font-weight: 800; font-size: 0.85rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.sp-rank-stars {
  flex: 0 0 auto; display: flex; align-items: center; gap: 2px;
  font-weight: 900; font-size: 0.95rem; text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.sp-rank-ico { display: inline-flex; }
.sp-rank-row.sp-rank-negative .sp-rank-num { color: #ffd2d2; }
.sp-rank-pos {
  flex: 0 0 auto; font-family: Georgia, "Times New Roman", serif;
  font-weight: 900; font-size: 1.15rem; font-style: italic;
  color: #fff; text-shadow: 0 2px 3px rgba(0,0,0,0.55);
  min-width: 34px; text-align: right;
}

/* ---- C2: wildcard reveal card (TACTIC-card style) ---- */
.sp-wc-card { text-align: center; }
.sp-wc-catlabel {
  font-size: 0.72rem; font-weight: 900; letter-spacing: 3px;
  color: var(--wc-color); margin-bottom: 10px;
}
.sp-wc-iconbox {
  width: 78px; height: 78px; margin: 0 auto 12px;
  border-radius: 20px; background: var(--wc-color);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 22px var(--wc-color), inset 0 -6px 10px rgba(0,0,0,0.2);
}
.sp-wc-icon { font-size: 2.6rem; line-height: 1; filter: drop-shadow(0 2px 3px rgba(0,0,0,0.4)); }
.sp-wc-big .sp-wc-iconbox { width: 92px; height: 92px; border-radius: 24px; }
.sp-wc-big .sp-wc-icon { font-size: 3rem; }
/* remove the old inline cat text position (now catlabel handles it) */
.sp-wc-cat { display: none; }


@media (max-width: 860px) {
  /* not enough room beside the felt: stack the panel above it instead of overlapping */
  .sp-table-layout { display: flex; flex-direction: column; }
  .sp-standings-panel {
    position: static; width: 100%;
    flex-direction: row; flex-wrap: wrap; gap: 6px;
    margin-bottom: 8px;
  }
  .sp-rank-row { flex: 1 1 150px; }
}

/* =====================================================
   STAR PARTY — golden wheel + blue GOOD + fit-on-screen
   (redesign round: CAMBIOS 1–8)
   ===================================================== */
/* GOOD category is now BLUE everywhere (CAMBIO 1) */
:root { --sp-good: #3b82f6; --sp-correct: #22c55e; }
/* ARREGLO 4: "correct answer" is its own dedicated green — it must NOT reuse
   --sp-good (that variable now means the wildcard GOOD category, which is blue). */
.sp-q-opt.correct { background: rgba(34, 197, 94, 0.32) !important; border-color: var(--sp-correct) !important; color: #d6ffe4 !important; }

/* ---- CAMBIO 2: everything fits without scrolling ---- */
.sp-hud { margin: 0 0 2px; }
.sp-round { padding: 4px 14px; font-size: 0.8rem; }
.sp-felt {
  width: min(92vw, calc(100vh - 175px), 620px);
  height: min(92vw, calc(100vh - 175px), 620px);
  margin: 2px auto 8px;
}
.sp-center.sp-mode-wheel { width: min(88%, 460px); }
.sp-wheel-wrap {
  width: min(56vw, calc(100vh - 350px), 320px);
  height: min(56vw, calc(100vh - 350px), 320px);
  margin: 22px 0 14px;
}
.sp-seat { width: 148px; }
.sp-standings-panel { width: 185px; top: 2px; left: 10px; }

/* ---- CAMBIO 1: golden wheel look ---- */
.sp-wheel {
  border: none;
  box-shadow: inset 0 0 34px rgba(0, 0, 0, 0.5);
}
.sp-wheel-rim {
  position: absolute; inset: -16px; border-radius: 50%; z-index: 0;
  background:
    conic-gradient(#8a5a0a, #f9e08a 12%, #b97e12 25%, #f3cf5e 40%, #8a5a0a 55%, #f9e08a 70%, #b97e12 85%, #8a5a0a);
  box-shadow:
    0 0 0 3px #5b3a06,
    0 8px 26px rgba(0, 0, 0, 0.6),
    0 0 34px rgba(246, 200, 90, 0.4),
    inset 0 0 14px rgba(0, 0, 0, 0.55);
}
.sp-wheel-lines { position: absolute; inset: 0; border-radius: 50%; z-index: 1; pointer-events: none; }
.sp-wheel-shine {
  position: absolute; inset: 0; border-radius: 50%; z-index: 1; pointer-events: none;
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.3) 0 12%, rgba(255, 255, 255, 0) 45%),
    radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0) 62%, rgba(0, 0, 0, 0.3) 100%);
}
.sp-wheel .sp-wheel-label { z-index: 2; }
.sp-wheel-bulbs { position: absolute; inset: -16px; border-radius: 50%; z-index: 4; pointer-events: none; }
.sp-wheel-bulb {
  position: absolute; width: 9px; height: 9px; margin: -4.5px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fffbe8, #ffd75e 55%, #b97e12);
  box-shadow: 0 0 8px 2px rgba(255, 215, 94, 0.85);
  animation: sp-bulb-twinkle 1.1s ease-in-out infinite;
}
@keyframes sp-bulb-twinkle {
  0%, 100% { box-shadow: 0 0 8px 2px rgba(255, 215, 94, 0.85); }
  50% { box-shadow: 0 0 3px 1px rgba(255, 215, 94, 0.35); }
}
.sp-wheel-hub {
  width: 56px; height: 56px;
  background: radial-gradient(circle at 38% 30%, #fff8dd, #f6c94f 42%, #b97e12 78%, #6b4708);
  border: 3px solid #f3d878;
  color: #7c5806; font-size: 1.25rem;
  box-shadow: 0 0 18px rgba(246, 200, 90, 0.9), 0 4px 10px rgba(0, 0, 0, 0.5), inset 0 -6px 10px rgba(0, 0, 0, 0.25);
}
.sp-wheel-pointer {
  top: -36px; left: 50%; margin-left: -16px;
  width: 32px; height: 32px;
  transform: rotate(45deg);
  border: 2px solid #7c5806;
  border-radius: 50% 50% 0 50%;
  background: radial-gradient(circle at 35% 30%, #fff7d6, #f2c94c 50%, #a8770e);
  box-shadow: 0 0 12px rgba(246, 200, 90, 0.85), inset 0 0 8px rgba(0, 0, 0, 0.25);
  filter: none;
  z-index: 6;
}
.sp-wheel-pointer::after {
  content: ""; position: absolute; inset: 7px;
  border-radius: 50% 50% 0 50%;
  background: radial-gradient(circle at 40% 35%, #ffe9a8, #b97e12);
}

/* ---- CAMBIO 3: wildcard card pops out CENTERED OVER the wheel ---- */
.sp-wheel-stage { position: relative; }
.sp-wc-big {
  position: absolute; top: 50%; left: 50%; margin: 0;
  width: min(92%, 320px); z-index: 9;
  animation: sp-card-pop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1) both !important;
}
@keyframes sp-card-pop {
  from { transform: translate(-50%, -50%) scale(0.05); opacity: 0; }
  to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}

/* ---- CAMBIO 5: Mario-Party-style standings bars ---- */
.sp-rank-row {
  padding: 6px 8px 6px 12px;
  border-radius: 9px;
  border: 2px solid rgba(255, 255, 255, 0.45);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--seat-color) 55%, #fff) 0%,
    var(--seat-color) 48%,
    color-mix(in srgb, var(--seat-color) 68%, #000) 100%);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.5);
}
.sp-rank-name { font-size: 0.82rem; }
.sp-rank-stars { gap: 3px; font-size: 0.9rem; }
.sp-rank-x { font-size: 0.7rem; font-weight: 900; opacity: 0.9; }
.sp-rank-pos {
  font-family: Georgia, "Times New Roman", serif; font-style: italic;
  display: flex; align-items: baseline; gap: 1px; justify-content: flex-end;
  min-width: 38px;
}
.sp-rank-pos b { font-size: 1.5rem; font-weight: 900; line-height: 1; text-shadow: 0 2px 3px rgba(0, 0, 0, 0.6); }
.sp-rank-pos small { font-size: 0.68rem; font-weight: 900; }

/* ---- CAMBIO 6: rotating turn arrow ---- */
.sp-turn-orbit {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  transition: transform 0.9s cubic-bezier(0.55, 0.05, 0.25, 1.2);
}
.sp-turn-arrow {
  /* ARREGLO 3: smaller orbit radius (was top:15%, too close to the seats/stars
     near the rim) — now orbits nearer the felt's center, clear of the stars */
  position: absolute; left: 50%; top: 28%;
  width: 26px; height: 26px; margin-left: -13px;
  transform: rotate(-135deg); /* sharp corner points UP, toward the seat */
  border-radius: 50% 50% 0 50%;
  background: radial-gradient(circle at 40% 60%, #fff7d6, #ffd75e 55%, #c8880a);
  border: 2px solid #7c5806;
  box-shadow: 0 0 14px rgba(255, 215, 94, 0.9);
  animation: sp-arrow-nudge 1.2s ease-in-out infinite;
}
@keyframes sp-arrow-nudge {
  0%, 100% { top: 28%; }
  50% { top: 30%; }
}

/* ---- CAMBIO 7 (rediseño): exact star count + decorative cluster, OUTSIDE and
   ABOVE the seat box (not inside it) — big number on the left, small overlapping
   scattered stars to its right, white/pink glow like the reference image. ---- */
.sp-seat-stars {
  position: absolute; bottom: calc(100% - 2px); left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 2px; pointer-events: none;
  white-space: nowrap;
}
/* ARREGLO 1: seats near the top edge of the circular felt would push these stars
   OFF the felt if placed above the box — flip them BELOW the box instead, toward
   the center, for any seat JS marks as "top" (topPct < 32). */
.sp-seat.sp-seat-top .sp-seat-stars { bottom: auto; top: calc(100% - 2px); }
.sp-seat-stars-num {
  font-weight: 900; font-size: 2.3rem; color: #fff; /* ARREGLO 4: bumped from 1.85rem */
  text-shadow: 0 0 10px rgba(255, 200, 230, 0.9), 0 2px 3px rgba(0, 0, 0, 0.7);
}
.sp-seat.negative .sp-seat-stars-num { color: #ff9a9a; text-shadow: 0 0 10px rgba(255, 100, 100, 0.85), 0 2px 3px rgba(0, 0, 0, 0.7); }
.sp-seat-stars-cluster { position: relative; display: inline-flex; height: 1.3em; }
.sp-hover-star {
  display: inline-block; line-height: 0;
  animation: sp-star-hover 2.2s ease-in-out infinite;
}
/* scattered/clumped placement instead of a straight row — each star tilted and
   offset a little differently, with slight overlap (actual sizes set via the
   icon's own width/height in JS — see hoverSizes in spUpdateSeats) */
.sp-hover-star-0 { margin-left: 2px; transform: rotate(-10deg) translateY(1px); }
.sp-hover-star-1 { margin-left: -6px; transform: rotate(9deg) translateY(-3px); }
.sp-hover-star-2 { margin-left: -5px; transform: rotate(-6deg) translateY(3px); }
@keyframes sp-star-hover {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-7px) scale(1.08); }
}

/* ---- CAMBIO 8: arc flight, trail, impact, seat bounce ---- */
.sp-fly { margin-left: -12px; margin-top: -12px; z-index: 8; }
.sp-fly-star { font-size: 1.6rem; color: #ffd75e; filter: drop-shadow(0 0 8px rgba(255, 215, 94, 0.95)); }
.sp-fly-num { font-size: 0.85rem; color: #fff; text-shadow: 0 1px 3px #000; }
.sp-fly-trail {
  position: absolute; width: 9px; height: 9px; margin: -4.5px;
  border-radius: 50%; pointer-events: none; z-index: 7;
  background: radial-gradient(circle, #fff8dd, #ffd75e 60%, rgba(255, 215, 94, 0));
  box-shadow: 0 0 10px rgba(255, 215, 94, 0.8);
  animation: sp-trail-fade 0.5s ease-out forwards;
}
@keyframes sp-trail-fade {
  from { opacity: 0.95; transform: scale(1); }
  to { opacity: 0; transform: scale(0.15); }
}
.sp-fly-impact {
  position: absolute; margin: -14px; pointer-events: none; z-index: 9;
  font-size: 1.7rem; color: #fff;
  text-shadow: 0 0 12px #ffd75e, 0 0 22px #ffd75e;
  animation: sp-impact-pop 0.45s ease-out forwards;
}
@keyframes sp-impact-pop {
  0% { opacity: 1; transform: scale(0.4); }
  45% { opacity: 1; transform: scale(1.5); }
  100% { opacity: 0; transform: scale(0.6); }
}
.sp-seat-inner.sp-hit { animation: sp-hit-squash 0.38s ease; }
@keyframes sp-hit-squash {
  0% { transform: scale(1, 1); }
  35% { transform: scale(1.14, 0.85); }
  70% { transform: scale(0.94, 1.07); }
  100% { transform: scale(1, 1); }
}

/* =====================================================
   STAR PARTY 2.0 — SLOT MACHINE + EMBEDDED MINIGAMES
   ===================================================== */

/* ---- the slot machine (start of every round) ---- */
.sp-slot-stage { text-align: center; }
.sp-slot {
  position: relative;
  width: 240px;
  margin: 10px auto 0;
  animation: sp-pop-out 450ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.sp-slot-top {
  position: relative;
  background: linear-gradient(180deg, #d92b2b, #a11212);
  border: 3px solid #f3cf5e;
  border-bottom: none;
  border-radius: 100px 100px 0 0;
  padding: 26px 10px 8px;
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.4) inset;
}
.sp-slot-sign {
  font-weight: 900; font-size: 0.85rem; letter-spacing: 2px; color: #ffe9a8;
  text-shadow: 0 0 8px rgba(255, 200, 60, 0.8), 0 2px 2px rgba(0, 0, 0, 0.6);
}
.sp-slot-bulbs { position: absolute; top: 4px; left: 0; right: 0; height: 40px; pointer-events: none; z-index: 2; }
.sp-slot-bulbs .sp-wheel-bulb { position: absolute; }
.sp-slot-body {
  position: relative;
  background: linear-gradient(180deg, #c11f1f, #7e0d0d);
  border: 3px solid #f3cf5e;
  border-top: 2px solid #f3cf5e;
  border-radius: 0 0 18px 18px;
  padding: 18px 16px 20px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55), inset 0 2px 10px rgba(255, 255, 255, 0.12);
}
.sp-slot-window {
  position: relative;
  width: 128px; height: 128px; margin: 0 auto;
  background: radial-gradient(circle at 50% 34%, #fffdf4, #efe6cd 75%);
  border: 4px solid #b97e12;
  border-radius: 14px;
  box-shadow: inset 0 4px 14px rgba(0, 0, 0, 0.35), 0 0 0 3px #f3cf5e;
  overflow: hidden;
}
/* the reel is a tall vertical strip; each cell fills the window and it
   translateY-scrolls upward (transform animated in JS with ease-out) */
.sp-slot-reel { display: flex; flex-direction: column; will-change: transform; }
.sp-slot-cell {
  width: 128px; height: 128px; flex: 0 0 128px;
  display: flex; align-items: center; justify-content: center;
}
.sp-slot-cell.landed .sp-slot-ico { animation: sp-slot-land 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes sp-slot-land {
  0% { transform: scale(0.72); }
  60% { transform: scale(1.14); }
  100% { transform: scale(1); }
}
/* subtle center guide line through the window (casino payline) */
.sp-slot-payline {
  position: absolute; left: 0; right: 0; top: 50%; height: 2px;
  transform: translateY(-50%);
  background: linear-gradient(90deg, transparent, rgba(168, 85, 247, 0.55), transparent);
  pointer-events: none;
}
.sp-slot-lever { position: absolute; right: -26px; top: 20px; width: 24px; height: 110px; }
.sp-slot-lever-arm {
  position: absolute; left: 9px; top: 16px; width: 7px; height: 74px;
  background: linear-gradient(90deg, #d8d8e0, #8b8b96);
  border-radius: 4px;
  transform-origin: bottom center;
  transition: transform 0.4s cubic-bezier(0.55, 0, 0.65, 1);
}
.sp-slot-lever-knob {
  position: absolute; left: -2px; top: 0; width: 28px; height: 28px; border-radius: 50%;
  background: radial-gradient(circle at 36% 30%, #ff8a8a, #d21c1c 60%, #7e0d0d);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  transition: transform 0.25s ease;
}
.sp-slot-lever.pulled .sp-slot-lever-arm { transform: scaleY(0.35); }
.sp-slot-lever.pulled .sp-slot-lever-knob { transform: translateY(48px); }
.sp-slot-name {
  margin-top: 12px; min-height: 1.4em;
  font-weight: 900; font-size: 1.15rem; letter-spacing: 1px; color: #ffe9a8;
  text-shadow: 0 0 10px rgba(255, 200, 60, 0.75), 0 2px 3px rgba(0, 0, 0, 0.6);
}

/* ---- minigame icons (slot window + risk header) ---- */
.sp-slot-ico {
  --s: 84px;
  width: var(--s); height: var(--s);
  display: inline-flex; align-items: center; justify-content: center;
  position: relative; line-height: 1;
}
/* trivia: mini golden wheel + side stars */
.sp-slot-trivia-wheel {
  width: 72%; height: 72%; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, #f6c94f 0 18%, rgba(0,0,0,0) 19%),
    conic-gradient(#3b82f6 0deg 30deg, #ef4444 30deg 60deg, #eab308 60deg 90deg,
      #3b82f6 90deg 120deg, #ef4444 120deg 150deg, #eab308 150deg 180deg,
      #3b82f6 180deg 210deg, #ef4444 210deg 240deg, #eab308 240deg 270deg,
      #3b82f6 270deg 300deg, #ef4444 300deg 330deg, #eab308 330deg 360deg);
  box-shadow: 0 0 0 calc(var(--s) * 0.05) #b97e12, 0 0 0 calc(var(--s) * 0.075) #f3cf5e, 0 2px 8px rgba(0, 0, 0, 0.45);
}
.sp-slot-trivia-star { position: absolute; top: 8%; }
.sp-slot-trivia-star.l { left: -4%; }
.sp-slot-trivia-star.r { right: -4%; }
/* bomb word */
.sp-slot-bomb-body { font-size: calc(var(--s) * 0.72); filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.5)); }
/* fill in the blanks: 4 cells, 1st solved blue, 2nd raised/active */
.sp-slot-fib { gap: calc(var(--s) * 0.05); }
.sp-slot-fib-cell {
  width: calc(var(--s) * 0.20); height: calc(var(--s) * 0.24);
  border: 2px solid #cbd5e1; border-radius: 4px; background: rgba(255, 255, 255, 0.12);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 900; font-size: calc(var(--s) * 0.15); color: #fff;
}
.sp-slot-fib-cell.done { background: #3b82f6; border-color: #60a5fa; }
.sp-slot-fib-cell.up { transform: translateY(calc(var(--s) * -0.09)); background: rgba(255, 255, 255, 0.28); border-color: #fff; }
/* is it a real word */
.sp-slot-real { flex-direction: column; gap: calc(var(--s) * 0.07); }
.sp-slot-real i { font-style: italic; font-weight: 800; font-size: calc(var(--s) * 0.24); color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.sp-slot-real-btns { display: flex; gap: calc(var(--s) * 0.07); }
.sp-slot-real-btns b {
  width: calc(var(--s) * 0.3); height: calc(var(--s) * 0.22); border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: calc(var(--s) * 0.14); color: #fff;
}
.sp-slot-real-btns b.y { background: #16a34a; }
.sp-slot-real-btns b.n { background: #dc2626; }
/* emoji bomb: tree + letter */
.sp-slot-emoji { flex-direction: column; }
.sp-slot-emoji-tree { font-size: calc(var(--s) * 0.5); filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); }
.sp-slot-emoji-letter { font-weight: 900; font-size: calc(var(--s) * 0.26); color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
/* word links */
.sp-slot-links { flex-direction: column; gap: 1px; }
.sp-slot-links small { font-weight: 900; font-size: calc(var(--s) * 0.13); letter-spacing: 1px; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.sp-slot-links-circle {
  width: calc(var(--s) * 0.42); height: calc(var(--s) * 0.42); border-radius: 50%;
  background: radial-gradient(circle at 36% 30%, #60a5fa, #1d4ed8 70%);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: calc(var(--s) * 0.22);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
/* icons rendered dark-on-light inside the slot window need their text flipped */
.sp-slot-window .sp-slot-real i,
.sp-slot-window .sp-slot-emoji-letter,
.sp-slot-window .sp-slot-links small { color: #1e1130; text-shadow: none; }
.sp-slot-window .sp-slot-fib-cell { border-color: #64748b; color: #1e1130; background: rgba(0, 0, 0, 0.06); }
.sp-slot-window .sp-slot-fib-cell.done { background: #3b82f6; color: #fff; }

/* ---- round-minigame reveal card (tactic-card component, informational) ----
   Reuses .sp-wc-card.sp-wc-big exactly; only the icon box gets a dark fill so the
   detailed multi-colour minigame icons stay legible (emoji wildcard icons sat on a
   flat colour box, these don't) while keeping the accent border + glow. */
.sp-slot-reveal .sp-wc-iconbox {
  background: radial-gradient(circle at 40% 30%, #2a1442, #150a24);
  box-shadow: 0 0 22px var(--wc-color), inset 0 -6px 10px rgba(0, 0, 0, 0.35);
}
.sp-slot-reveal .sp-wc-icon { font-size: 1rem; } /* icon sizes itself via --s; don't inflate it */

/* ---- round-game label on the risk screen ---- */
.sp-round-game {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin: 4px 0 10px;
  font-weight: 900; font-size: 1.05rem; letter-spacing: 1px; color: #fff;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

/* ---- embedded minigame shell ---- */
.sp-mg-stage { width: 100%; }
.sp-mg-host { width: 100%; }
.sp-mg { width: 100%; text-align: center; }
.sp-mg-head {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin-bottom: 8px; flex-wrap: wrap;
}
.sp-mg-title { font-weight: 900; font-size: 1rem; letter-spacing: 1px; color: #fff; }
.sp-mg-lives .bw-heart { font-size: 0.95rem; }
.sp-mg-1try {
  font-weight: 900; font-size: 0.72rem; letter-spacing: 1px; color: #fecaca;
  border: 1px solid #ef4444; border-radius: 999px; padding: 2px 10px;
}
.sp-mg-prog { font-weight: 800; font-size: 0.85rem; color: var(--text-dim); }
.sp-mg-timer {
  position: relative; height: 12px; border-radius: 6px; overflow: hidden;
  background: rgba(255, 255, 255, 0.12); margin: 0 auto 12px; max-width: 420px;
}
.sp-mg-timer-fill {
  height: 100%; width: 100%; border-radius: 6px;
  background: linear-gradient(90deg, var(--sp-accent), #d8b4fe);
  transition: width 0.1s linear;
}
.sp-mg-timer-fill.urgent { background: linear-gradient(90deg, #ef4444, #f87171); }
.sp-mg-seconds {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  font-weight: 900; font-size: 0.62rem; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.8);
}
.sp-mg-body { min-height: 120px; }
.sp-mg-prompt { margin: 6px 0 12px; }
.sp-mg-prefix {
  font-weight: 900; font-size: 2.6rem; letter-spacing: 6px; color: #fff;
  text-shadow: 0 0 14px rgba(168, 85, 247, 0.8), 0 2px 4px rgba(0, 0, 0, 0.6);
}
.sp-mg-realword i { font-style: italic; font-weight: 800; font-size: 2.2rem; color: #fff; }
.sp-mg-emoji { display: flex; align-items: center; justify-content: center; gap: 16px; }
.sp-mg-emoji-letter {
  font-weight: 900; font-size: 2.2rem; color: #fff;
  border: 2px solid var(--sp-accent); border-radius: 10px; padding: 2px 14px;
  text-shadow: 0 0 10px rgba(168, 85, 247, 0.8);
}
.sp-mg-emoji-icons span { font-size: 2rem; margin: 0 4px; }
.sp-mg-sentence { font-size: 1.15rem; color: var(--text); margin-bottom: 10px; line-height: 1.5; }
.sp-mg-sentence .gap { color: var(--accent-bright); font-weight: 800; letter-spacing: 2px; }
.sp-mg-cells { display: flex; justify-content: center; gap: 5px; flex-wrap: wrap; margin-bottom: 12px; }
.sp-mg-cells .letter-cell { width: 34px; height: 40px; font-size: 1.1rem; }
.sp-mg-cells .sp-mg-hidden { background: rgba(255, 255, 255, 0.10); border: 2px dashed rgba(255, 255, 255, 0.35); border-radius: 6px; }
.sp-mg-wl-clues { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.sp-mg-wl-meta { font-weight: 700; font-size: 0.85rem; color: var(--text-dim); margin-bottom: 10px; }
.sp-mg-form { display: flex; justify-content: center; gap: 8px; margin-top: 4px; }
.sp-mg-input {
  width: min(260px, 60%); padding: 10px 14px; border-radius: 10px;
  border: 2px solid rgba(168, 85, 247, 0.55); background: rgba(0, 0, 0, 0.35);
  color: #fff; font-weight: 800; font-size: 1.05rem; letter-spacing: 1px; text-transform: uppercase;
  outline: none;
}
.sp-mg-input:focus { border-color: var(--sp-accent); box-shadow: 0 0 12px rgba(168, 85, 247, 0.45); }
.sp-mg-fb { min-height: 1.5em; margin-top: 10px; font-weight: 800; font-size: 1rem; }
.sp-mg-fb.ok { color: var(--sp-correct, #22c55e); }
.sp-mg-fb.bad { color: var(--sp-bad); }
.sp-mg-rw-btns { display: flex; justify-content: center; gap: 14px; }
.sp-mg-rw-btn {
  min-width: 110px; padding: 12px 18px; border-radius: 12px; border: none; cursor: pointer;
  font-weight: 900; font-size: 1.05rem; color: #fff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  transition: transform 0.12s ease;
}
.sp-mg-rw-btn:hover:not(:disabled) { transform: translateY(-2px); }
.sp-mg-rw-btn.yes { background: linear-gradient(180deg, #22c55e, #15803d); }
.sp-mg-rw-btn.no { background: linear-gradient(180deg, #ef4444, #b91c1c); }
.sp-mg-rw-btn.picked { outline: 3px solid #fff; }
.sp-mg-rw-btn:disabled { opacity: 0.75; cursor: default; }
.sp-mg-result {
  margin-top: 14px; font-weight: 900; font-size: 1.5rem; letter-spacing: 1px;
  animation: sp-pop-out 400ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.sp-mg-result small { display: block; font-size: 0.85rem; font-weight: 700; color: var(--text-dim); margin-top: 4px; letter-spacing: 0; }
.sp-mg-result.won { color: var(--sp-correct, #22c55e); text-shadow: 0 0 14px rgba(34, 197, 94, 0.5); }
.sp-mg-result.lost { color: var(--sp-bad); text-shadow: 0 0 14px rgba(239, 68, 68, 0.5); }

/* ================= EMOJI MATCH ================= */
.em-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  padding: 10px 16px 30px;
  max-width: 560px;
  margin: 0 auto;
  width: 100%;
}

/* the 3 emojis: big and centred. clamp() keeps them large on desktop without
   overflowing a 360px phone, and they wrap rather than force a scrollbar. */
.em-emojis {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: clamp(10px, 4vw, 26px);
  padding: clamp(16px, 5vw, 30px) 14px;
  width: 100%;
  border-radius: 18px;
  background: var(--tile);
  border: 1px solid var(--line-soft);
}
.em-emoji {
  font-size: clamp(2.6rem, 13vw, 4.4rem);
  line-height: 1.1;
}

/* 4 big option buttons. Single column on phones (full width, easy to hit),
   two columns once there's room. */
.em-options {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  width: 100%;
}
.em-option {
  min-height: 52px;
  padding: 12px 14px;
  border-radius: 12px;
  border: 2px solid var(--line-soft);
  background: var(--tile);
  color: var(--text);
  font-family: inherit;
  font-size: clamp(0.95rem, 3.6vw, 1.15rem);
  font-weight: 800;
  letter-spacing: 1px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.12s;
  /* long words (RESTAURANT, UNIVERSITY) must wrap inside the button instead of
     widening it past the viewport */
  overflow-wrap: anywhere;
}
.em-option:hover:not(:disabled),
.em-option:focus-visible:not(:disabled) {
  background: rgba(168, 85, 247, 0.28);
  border-color: var(--accent-bright);
  transform: translateY(-1px);
}
.em-option:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 2px; }
.em-option:disabled { cursor: default; }

/* answer reveal: the correct word always turns green, the wrong pick turns red */
.em-option.correct {
  background: var(--tile-green);
  border-color: var(--tile-green);
  color: #fff;
}
.em-option.wrong {
  background: #b3423f;
  border-color: #b3423f;
  color: #fff;
  opacity: 0.9;
}

/* end screen reuses the Strands chip + shared meaning panel. Override the
   line-through Strands puts on "missed": here the word IS a real, correct
   answer — the player just picked the wrong option — so striking it out would
   wrongly read as "this word is invalid". Tint only. */
#em-wordlist li.missed .st-word-chip {
  text-decoration: none;
  opacity: 1;
}
#em-wordlist li.missed .st-word-btn {
  text-decoration: underline dotted rgba(217, 154, 154, 0.6);
  text-underline-offset: 3px;
}

@media (min-width: 560px) {
  .em-options { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 480px) {
  .em-wrap { gap: 18px; padding: 8px 12px 26px; }
  /* 44px minimum tap target on touch screens (these are comfortably above it) */
  .em-option { min-height: 50px; }
}

/* ================= HEAR IT ================= */
.hi-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 10px 16px 30px;
  max-width: 560px;
  margin: 0 auto;
  width: 100%;
}
.hi-wrap.hidden { display: none; }

/* big round play button — the audio trigger and the visual centre of the screen */
.hi-play-btn {
  width: clamp(96px, 30vw, 132px);
  height: clamp(96px, 30vw, 132px);
  border-radius: 50%;
  border: 3px solid var(--accent-bright, #d846ef);
  background: radial-gradient(circle at 38% 32%, rgba(168, 85, 247, 0.45), rgba(29, 5, 48, 0.9));
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 22px rgba(168, 85, 247, 0.45);
  transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s;
}
.hi-play-btn:hover:not(:disabled),
.hi-play-btn:focus-visible:not(:disabled) {
  transform: scale(1.05);
  box-shadow: 0 0 32px rgba(168, 85, 247, 0.7);
}
.hi-play-btn:active:not(:disabled) { transform: scale(0.97); }
.hi-play-btn:focus-visible { outline: 3px solid var(--accent-bright); outline-offset: 4px; }
.hi-play-icon {
  font-size: clamp(2.2rem, 9vw, 3rem);
  line-height: 1;
  margin-left: 6px; /* optical centring: a ▶ glyph sits visually left of centre */
}
/* out of listens: clearly spent, not just unresponsive */
.hi-play-btn.spent,
.hi-play-btn:disabled {
  cursor: default;
  opacity: 0.4;
  border-color: var(--line-soft);
  box-shadow: none;
  transform: none;
}

.hi-replays {
  margin: 0;
  font-size: 0.78rem;
  letter-spacing: 1px;
  color: var(--text-dim);
  text-align: center;
}
.hi-letters {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 2px;
  color: #cdeef7;
  text-align: center;
}

/* BASIC: 4 options, same look as Emoji Match so the portal stays consistent */
.hi-options {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  width: 100%;
}
.hi-options.hidden { display: none; }
.hi-option {
  min-height: 52px;
  padding: 12px 14px;
  border-radius: 12px;
  border: 2px solid var(--line-soft);
  background: var(--tile);
  color: var(--text);
  font-family: inherit;
  font-size: clamp(0.95rem, 3.6vw, 1.15rem);
  font-weight: 800;
  letter-spacing: 1px;
  cursor: pointer;
  overflow-wrap: anywhere;
  transition: background 0.15s, border-color 0.15s, transform 0.12s;
}
.hi-option:hover:not(:disabled),
.hi-option:focus-visible:not(:disabled) {
  background: rgba(168, 85, 247, 0.28);
  border-color: var(--accent-bright);
  transform: translateY(-1px);
}
.hi-option:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 2px; }
.hi-option:disabled { cursor: default; }
.hi-option.correct { background: var(--tile-green); border-color: var(--tile-green); color: #fff; }
.hi-option.wrong { background: #b3423f; border-color: #b3423f; color: #fff; opacity: 0.9; }

/* INTERMEDIATE / ADVANCED: type the word */
.hi-input-wrap {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}
.hi-input-wrap.hidden { display: none; }
#hi-input {
  width: 100%;
  min-height: 52px;
  padding: 12px 14px;
  border-radius: 12px;
  border: 2px solid var(--line-soft);
  background: rgba(0, 0, 0, 0.3);
  color: #fff;
  font-family: inherit;
  font-size: clamp(1rem, 4.5vw, 1.3rem);
  font-weight: 700;
  letter-spacing: 2px;
  text-align: center;
  text-transform: uppercase;
}
#hi-input:focus { outline: none; border-color: var(--accent-bright); box-shadow: 0 0 14px rgba(168, 85, 247, 0.4); }
#hi-input:disabled { opacity: 0.6; }
#hi-submit { min-height: 48px; }

.hi-feedback {
  min-height: 26px;
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  text-align: center;
  overflow-wrap: anywhere;
}
.hi-feedback.ok { color: #9ae6a0; }
.hi-feedback.bad { color: #f0a0a0; }

/* device has no English voice at all: a discreet one-line note, no box/icon/button —
   the game stays fully playable, this is purely informational */
.hi-novoice-note {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-dim);
  text-align: center;
}
.hi-novoice-note.hidden { display: none; }

/* end screen: the phonetic-contrast explanation under a missed minimal pair */
.hi-contrast {
  margin: 8px 0 0;
  padding: 8px 10px;
  border-radius: 8px;
  border-left: 3px solid var(--accent-bright, #d846ef);
  background: var(--tile);
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--text);
}
.hi-replay-btn {
  margin-top: 10px;
  padding: 7px 14px;
  min-height: 36px;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  background: var(--tile);
  color: var(--text);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
}
.hi-replay-btn:hover, .hi-replay-btn:focus-visible { background: rgba(168, 85, 247, 0.32); color: #fff; }

/* a missed word here is still a perfectly real word — the player just heard it
   wrong — so tint it without the line-through Strands uses */
#hi-wordlist li.missed .st-word-chip { text-decoration: none; opacity: 1; }
#hi-wordlist li.missed .st-word-btn {
  text-decoration: underline dotted rgba(217, 154, 154, 0.6);
  text-underline-offset: 3px;
}

@media (min-width: 560px) {
  .hi-options { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 480px) {
  .hi-wrap { gap: 14px; padding: 8px 12px 26px; }
  .hi-option { min-height: 50px; }
  #hi-input { min-height: 50px; }
  .hi-replay-btn { min-height: 44px; padding: 10px 16px; }
}

/* =====================================================
   ACHIEVEMENTS — card medals, header counter, and the badge screen.

   🚨 La medalla cuelga de .game-card, NO de .card-art, y eso NO es un descuido
   que haya que "arreglar" ahora que el marco esta en .card-art: esa caja tiene
   `overflow: hidden` y la recortaria por la esquina redondeada. Lo pone tambien
   app.js donde la inserta («direct child: .card-art has overflow:hidden»), asi
   que moverla no seria un cambio de CSS. Esquina superior DERECHA, en espejo de
   la pastilla verde «NEW» de la izquierda, para que no se pisen.

   top:0 alinea su borde superior con el del marco, colgando como un marcapaginas
   en vez de flotar por fuera.

   🚨 EL `right` VA ATADO A DONDE ESTE EL BORDE, y ya ha cambiado tres veces:
   la caja contra la que se posiciona es la de RELLENO de .game-card, o sea por
   dentro de su borde. El objetivo es siempre el mismo: 14 px desde el borde
   exterior del marco, que es el `border-radius`.

       borde de 1px en .game-card ....... right: 13px
       borde de 4px en .game-card ....... right: 10px
       SIN borde en .game-card (hoy) .... right: 14px   <- el marco esta en
                                                           .card-art, y como
                                                           esta ocupa todo el
                                                           ancho, el borde de
                                                           .game-card ya no
                                                           descuenta nada

   Medido tras el cambio: 14,0 px exactos, y no la recorta nadie porque
   .game-card es `overflow: visible`. */
.card-medal {
  position: absolute;
  top: 0;
  right: 14px;
  width: 30px;
  height: 30px;
  object-fit: contain;
  z-index: 3;
  pointer-events: none;
}
/* same escape hatch as .card-art-img-failed: a missing PNG must not leave a
   broken-image glyph pinned to the corner of the card */
.card-medal-failed { display: none; }

/* ---- header counter (hidden off-home, exactly like #home-sfx-control) ---- */
.badge-counter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 6px;
  border: 1px solid var(--panel-border);
  border-radius: 999px;
  background: var(--panel-85);
}
.badge-counter-icon { width: 20px; height: 20px; object-fit: contain; }
.badge-counter-text {
  font-size: 0.85rem;
  font-weight: 800;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- trophy + Instagram, beside the tagline ----
   Both home-only (unlike the header's badge counter/sound control, which are
   site-wide and toggled by showScreen()) — .tagline-row only exists inside
   #screen-home, so neither button needs any visibility logic of its own. */
.tagline-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 40px 16px 28px;
}
.tagline-row .tagline { margin: 0; }

/* shared chrome for the two buttons beside the tagline — size, border,
   background, hover lift + glow, focus ring. Each button keeps only what is
   actually its own (achievements needs position:relative for the red dot;
   the Instagram link just sets its icon's resting color). */
.tagline-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 46px;
  height: 46px;
  min-width: 44px;
  min-height: 44px;
  padding: 6px;
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  background: var(--panel-85);
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
}
.tagline-btn:hover, .tagline-btn:focus-visible { transform: translateY(-2px); box-shadow: 0 0 16px rgba(155, 48, 255, 0.5); }
.tagline-btn:focus-visible { outline: 2px solid var(--accent-bright, #d846ef); outline-offset: 2px; }

.achievements-btn-img { width: 100%; height: 100%; object-fit: contain; display: block; }

.social-link { color: var(--text-dim); }
.social-link:hover, .social-link:focus-visible { color: var(--accent-bright); }
.social-link-icon { width: 24px; height: 24px; display: block; }

.achievements-dot {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #ff3b57;
  border: 2px solid var(--bg-1);
  animation: achDot 1.4s ease-in-out infinite;
}
@keyframes achDot { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.25); } }

/* ---- the screen ---- */
.ach-head { text-align: center; margin: 6px auto 26px; padding: 0 16px; }
.ach-title { font-size: 1.8rem; letter-spacing: 2px; margin: 0 0 6px; }
.ach-subtitle { color: var(--text-dim); margin: 0 0 10px; }
.ach-progress {
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--accent-bright);
  font-variant-numeric: tabular-nums;
  margin: 0 0 14px;
}

/* switch: hides medals + counter, never stops recording */
.ach-switch {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  color: var(--text-dim);
  font-size: 0.95rem;
  user-select: none;
}
.ach-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ach-switch-track {
  width: 44px;
  height: 24px;
  border-radius: 999px;
  background: var(--tile);
  border: 1px solid var(--line-soft);
  padding: 2px;
  display: block;
  transition: background 0.18s ease;
}
.ach-switch-knob {
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--text-dim);
  transition: transform 0.18s ease, background 0.18s ease;
}
.ach-switch input:checked + .ach-switch-track { background: var(--accent); }
.ach-switch input:checked + .ach-switch-track .ach-switch-knob { transform: translateX(20px); background: #fff; }
.ach-switch input:focus-visible + .ach-switch-track { outline: 2px solid var(--accent-bright); outline-offset: 2px; }

/* ---- groups ---- */
.ach-groups {
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 16px 50px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}
.ach-group {
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: 16px;
  padding: 16px 16px 18px;
}
.ach-group-title {
  margin: 0 0 14px;
  font-size: 1.1rem;
  font-weight: 800;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 10px;
}
.ach-group-count {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.ach-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}

/* ---- one badge ---- */
.ach-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  padding: 12px 8px 10px;
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.22);
}
.ach-badge-img { width: 62px; height: 62px; object-fit: contain; }
.ach-badge-img-failed { visibility: hidden; }
.ach-badge-name { font-size: 0.85rem; font-weight: 800; line-height: 1.2; }
.ach-badge-goal { font-size: 0.75rem; color: var(--text-dim); line-height: 1.3; }
.ach-badge-progress {
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--accent-bright);
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.ach-badge-state { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.5px; }

.ach-badge.is-locked .ach-badge-img,
.ach-badge.is-pending .ach-badge-img { filter: grayscale(1) brightness(0.5); opacity: 0.45; }
.ach-badge.is-locked .ach-badge-name { color: var(--text-dim); }
.ach-badge.is-locked .ach-badge-state { color: #7d6a8d; }

/* earned but a prior tier of the chain is still unclaimed: a notch above
   locked (dim art stays dim — nothing to reclaim yet), a notch below ready
   (no glow, no pulse, no CLAIM button — that's reserved for actually
   actionable badges). A faint version of the ready-state gold ties the two
   together visually: "close" without shouting "claim me now". */
.ach-badge.is-pending {
  border-color: rgba(255, 207, 60, 0.35);
  background: rgba(255, 207, 60, 0.06);
}
.ach-badge.is-pending .ach-badge-state { color: #d9ab3c; }

.ach-badge.is-claimed { border-color: var(--panel-border); }
.ach-badge.is-claimed .ach-badge-state { color: #23d160; }

.ach-badge.is-ready {
  border-color: #ffcf3c;
  box-shadow: 0 0 14px rgba(255, 207, 60, 0.35);
  animation: achReady 1.8s ease-in-out infinite;
}
@keyframes achReady {
  0%, 100% { box-shadow: 0 0 10px rgba(255, 207, 60, 0.28); }
  50% { box-shadow: 0 0 20px rgba(255, 207, 60, 0.55); }
}
.ach-claim-btn {
  margin-top: 2px;
  padding: 7px 14px;
  min-height: 36px;
  border: none;
  border-radius: 9px;
  background: #ffcf3c;
  color: #3a2600;
  font-weight: 800;
  font-size: 0.78rem;
  letter-spacing: 0.5px;
  cursor: pointer;
}
.ach-claim-btn:hover { background: #ffdd74; }

@media (prefers-reduced-motion: reduce) {
  .achievements-dot, .ach-badge.is-ready { animation: none; }
}

/* ---- mobile ---- */
@media (max-width: 768px) {
  .card-medal { width: 26px; height: 26px; top: 0; right: 14px; } /* sin borde en .game-card: ver arriba */
  .ach-row { grid-template-columns: repeat(2, 1fr); }
  .ach-badge-img { width: 56px; height: 56px; }
  .ach-title { font-size: 1.5rem; }
  .badge-counter { padding: 3px 8px 3px 5px; }
  .badge-counter-icon { width: 17px; height: 17px; }
  .badge-counter-text { font-size: 0.78rem; }
}
@media (max-width: 480px) {
  /* At this width the header has zero slack beside the 220px logo: counter and
     logo overlap by ~64px. The number is one tap away on the achievements
     screen, and the trophy button (with its red dot) stays on the tagline row.
     Later in the file than .hidden, so this wins without !important. */
  .badge-counter { display: none; }
  .card-medal { width: 23px; height: 23px; top: 0; right: 14px; } /* sin borde en .game-card: ver arriba */
  .tagline-row { gap: 10px; margin: 26px 12px 20px; }
  /* .tagline-btn keeps its 44px min-width/min-height floor here (the tap
     target rule wins over any smaller width/height) — there's no overlap at
     this width, so there is nothing to shrink. */
  .social-link-icon { width: 21px; height: 21px; }
  .ach-groups { padding: 0 10px 40px; gap: 18px; }
  .ach-group { padding: 12px 10px 14px; }
  .ach-badge { padding: 10px 6px 8px; }
  .ach-badge-img { width: 48px; height: 48px; }
  .ach-badge-name { font-size: 0.78rem; }
  .ach-badge-goal { font-size: 0.7rem; }
}

/* =====================================================
   LEVEL TEST
   Sober on purpose: the result screen gets redesigned later, so the value
   here is the id/class structure, not the styling. Desktop rules only —
   phone adjustments live in the 768/480 media queries further down.
   ===================================================== */

/* ---- home entry strip ---- */
/* Not a .game-card: the test is not played daily, and inside the grid it
   would read as a fourteenth game. Its own strip above the grid is also the
   only shape with room for the saved score inline. */
.level-entry {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: min(720px, calc(100% - 32px));
  margin: 0 auto 26px;
  /* El padding vertical es corto a proposito: el arco es lo mas alto que hay
     dentro y manda la altura de la barra. Con 14px la barra salia en 94px
     (relacion 7,6:1) contra los 10:1 del mockup. */
  padding: 6px 20px;
  border: 1px solid var(--panel-border);
  border-radius: 16px;
  background: var(--entry-grad);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.level-entry:hover,
.level-entry:focus-visible { transform: translateY(-2px); box-shadow: 0 0 18px rgba(155, 48, 255, 0.45); }
.level-entry:focus-visible { outline: 2px solid var(--accent-bright, #d846ef); outline-offset: 2px; }

/* La imagen NUNCA se deforma: solo se declara el ancho y el alto sale de la
   relacion 6,383 del archivo. width:100% + max-width la deja encogerse sola
   cuando el espacio aprieta, en vez de desbordar o recortarse. Nada de height
   fijo aqui ni en las media queries. */
.level-bar-img {
  display: block;
  width: 100%;
  max-width: 240px;
  height: auto;
  flex: 0 1 auto;
  min-width: 0;
}

/* Hueco del arco. Lo rellena lvRefreshEntry() con un clon; --gauge-w es lo
   unico que cambia entre la barra y la pantalla de «mi nivel». */
.level-bar-gauge { flex: none; display: block; }
.level-bar-gauge .level-gauge-wrap { width: var(--gauge-w, 100px); }

/* ---- 1. entry screen ---- */
/* 🚨 LAS REGLAS .hidden DE ESTE BLOQUE — no borrar ninguna.
   Este proyecto NO tiene una regla generica `.hidden { display: none }`: cada
   componente declara la suya (hay 33 repartidas por el archivo). El codigo del
   test de nivel daba por hecho que existia la generica, asi que TODOS estos
   elementos se pintaban con .hidden puesta: los dos paneles a la vez, los
   avisos vacios con su borde, y el boton de audio visible en las 24 preguntas.
   Al anadir un elemento nuevo aqui que se apague con .hidden, anadir tambien
   su regla. */
/* 🚨 Sin esta regla, esconder la cabecera desde showScreen() no haria nada: en
   este proyecto .hidden no oculta por si sola. */
.topbar.hidden { display: none; }

.level-intro.hidden,
.level-warning.hidden,
.level-prompt.hidden,
.level-audio-count.hidden,
.level-q-sub.hidden,
.level-speaker-btn.hidden,
.level-chat.hidden,
.game-meta.hidden { display: none; }

.level-intro {
  max-width: 560px;
  margin: 20px auto;
  padding: 30px 24px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
  text-align: center;
}
.level-title { font-size: 1.5rem; font-weight: 900; letter-spacing: 3px; margin-bottom: 16px; }
.level-desc { font-size: 0.95rem; line-height: 1.6; color: var(--text-dim); margin-bottom: 22px; }

/* the saved block is the ONLY thing .has-level toggles — one layout, two states */
.level-saved { display: none; margin-bottom: 20px; }
.level-intro.has-level .level-saved { display: block; }
.level-saved-score { font-size: 3rem; font-weight: 900; line-height: 1; color: var(--accent-bright); }
.level-saved-of { font-size: 0.9rem; font-weight: 700; color: var(--text-dim); margin-left: 8px; }
.level-saved-cefr { font-size: 1.05rem; font-weight: 800; margin-top: 4px; }
.level-saved-date { font-size: 0.82rem; color: var(--text-dim); margin-top: 8px; }
.level-saved-summary { font-size: 0.85rem; color: var(--text-dim); margin-top: 6px; }

/* used for the repeat notice, the no-voices notice and the load error. Never
   blocks anything — it only informs. */
.level-warning {
  font-size: 0.85rem;
  line-height: 1.5;
  color: #f0d47a;
  background: rgba(245, 197, 24, 0.1);
  border: 1px solid rgba(245, 197, 24, 0.45);
  border-radius: 10px;
  padding: 10px 12px;
  margin-bottom: 18px;
}


/* =====================================================
   LEVEL TEST — LA PANTALLA DE PREGUNTA (#screen-level-test)

   Todo cuelga del ancho de la BARRA de progreso (B), igual que en el mockup.
   Contenedor 900 − 40 de padding = 860 de interior; menos la X (44) y su hueco
   (20) quedan B = 796. Las proporciones del mockup aplicadas a ese B están en
   el reporte, con sus porcentajes.
   ===================================================== */

/* ---- cabecera: la X y la barra en una sola fila ---- */
.level-test-header {
  display: flex;
  align-items: center;
  gap: 20px;
  width: 100%;
  max-width: 900px;
  /* padding-top y NO margin-top: el margen de un hijo de <main> se colapsa
     hacia arriba y se escapa del body, que tiene min-height:100vh — sumaba
     20px al alto de la pagina y provocaba scroll con el contenido cabiendo. */
  margin: 0 auto;
  padding: 20px 20px 0;
}
/* 44x44 y no los 21px que darían las proporciones del mockup (2,6% de B): por
   debajo de 44 el objetivo táctil no cumple. El aspa dibujada sí conserva su
   tamaño relativo — 22px de glifo dentro del botón. */
.level-close-btn {
  flex: none;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  background: none; border: none;
  color: var(--accent-bright);
  border-radius: 10px;
}
.level-close-btn svg { width: 22px; height: 22px; }
.level-close-btn:hover { color: #fff; }
.level-close-btn:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 2px; }

/* ---- el contenedor ---- */
.level-test-wrap { max-width: 900px; margin: 0 auto; padding: 0 20px 28px; }

/* Carril redondeado CON borde y el relleno dentro, no una línea maciza. 18px es
   el 2,26% de B: el mockup pedía 4,30% (34px, demasiado grueso junto a una
   cabecera de 44) y el video de referencia 1,6% (13px, que con el borde de 1px
   se lee como un hilo). Antes eran 8px. */
.level-progress {
  flex: 1 1 auto;
  min-width: 0;
  height: 18px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--panel-border);
}
.level-progress-fill {
  height: 100%; width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-bright));
  transition: width 0.25s ease;
}

/* ---- el título por tipo de ejercicio ---- */
/* Altura de mayúscula 3,71% de B = 29,5px; con una cap-height de ~0,72em eso
   sale a ~41px. Alineado a la izquierda, a ras de la barra. */
.level-test-title {
  font-size: 2.5rem;
  font-weight: 900;
  line-height: 1.15;
  text-align: left;
  /* 32 y no los 59 del mockup (7,4% de B): es la segunda palanca de altura
     despues de esconder la cabecera, y la unica que no toca ni las opciones ni
     el min-height de la zona. Ver el presupuesto vertical en el reporte. */
  margin: 32px 0 24px;
}

/* ---- la zona de contenido ---- */
/* 🚨 LA REGLA DE LAYOUT DE ESTA PANTALLA.
   min-height, no height: la zona absorbe desde una definición de una línea
   hasta un grammar_error de cuatro frases SIN que las opciones se muevan. Si
   bailan, alguien responde lo que no quería. El valor es MEDIDO — ver el
   reporte, que trae la altura de las cuatro maquetaciones. */
.level-prompt-zone {
  min-height: 180px; /* medido: el bloque de chat, el mas alto de los cuatro */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 24px;
}
.level-prompt {
  font-size: 1.35rem;
  line-height: 1.5;
  text-align: center;
  white-space: pre-line; /* grammar_error ships its four sentences newline-separated */
}
/* grammar_error y grammar_best traen su pregunta dentro del item y el título ya
   la dice, así que en esos dos el enunciado no se pinta. */
.level-prompt.hidden { display: none; }

/* (a) el hueco de collocation / grammar_gap, como hueco de verdad y no como
   tres guiones bajos crudos */
.level-blank {
  display: inline-block;
  width: 3.2em;
  border-bottom: 2px solid var(--text-dim);
  /* baja la línea hasta donde apoyaría el texto, sin mover la línea base */
  transform: translateY(-0.15em);
}

/* (b) synonym: la palabra resaltada. SOLO cambia el color — mismo tamaño y
   mismo peso que el resto de la frase. Token propio: --accent-bright lo usa
   media UI y no se toca. */
.level-syn-word { color: var(--accent-soft); }
/* Mismo tamano que la frase del ejercicio (.level-prompt), no mas pequeno: es
   el enunciado de la tarea, no una nota al pie. Nombre neutro porque ya no es
   exclusivo de synonym — lo llevan cuatro formatos. */
.level-q-sub {
  font-size: 1.35rem;
  font-weight: 800;
  line-height: 1.5;
  text-align: center;
  margin-bottom: 10px;
}
.level-q-sub.hidden { display: none; }

/* (d) la bocina grande de listen_word / listen_sentence.
   14,8% x 10,5% de B = 118 x 84. VOLUME.webp ya se descarga en la portada para
   el control de sonido, así que aquí viene de caché. */
.level-speaker-btn {
  width: 118px; height: 84px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  border: none; border-radius: 18px;
  background: var(--speaker-bg);
  transition: transform 0.12s ease, filter 0.12s ease;
}
.level-speaker-btn:hover { filter: brightness(1.15); transform: translateY(-2px); }
.level-speaker-btn:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 3px; }
.level-speaker-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
.level-speaker-btn svg { width: 52%; height: auto; fill: var(--speaker-fg); color: var(--speaker-fg); }
.level-speaker-btn.hidden { display: none; }

/* (e) listen_question: las dos burbujas. Alto del bloque 14,9% de B = 119. */
/* gap 6 y no 14: en el mockup las dos filas casi se tocan — 7px sobre un bloque
   de 1220, o sea el 0,6%, que en nuestros 860 son 5,2. */
.level-chat { width: 100%; display: flex; flex-direction: column; gap: 6px; }
.level-chat.hidden { display: none; }
.level-chat-row { display: flex; align-items: center; gap: 10px; }
.level-chat-row-left { justify-content: flex-start; }
.level-chat-row-right { justify-content: flex-end; }
/* 8,6% del bloque del mockup = 74px en nuestros 860 */
.level-chat-avatar {
  flex: none; width: 72px; height: 72px;
  display: flex; align-items: center; justify-content: center;
}
.level-chat-avatar svg { width: 100%; height: 100%; fill: #fff; }
/* 32,4% x 8,9% del bloque del mockup = 278,6 x 76,5 en nuestros 860 */
.level-chat-bubble {
  display: flex; align-items: flex-end; gap: 10px;
  min-width: 280px; min-height: 72px;
  /* padding-bottom corto: es lo que baja la rayita hasta el 88% de la altura de
     la burbuja, que es donde cae en el mockup. */
  padding: 10px 16px 7px;
  border: 1px solid var(--panel-border);
  border-radius: 12px;
}
.level-chat-speaker {
  flex: none; align-self: flex-end; margin-bottom: 2px;
  width: 42px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  border: none; border-radius: 8px;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}
.level-chat-speaker svg { width: 22px; height: 22px; fill: currentColor; }
.level-chat-speaker:hover { background: rgba(255, 255, 255, 0.22); }
.level-chat-speaker:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 2px; }
.level-chat-speaker:disabled { opacity: 0.45; cursor: not-allowed; }
/* La rayita que representa lo que se oye / lo que se va a responder. Va PEGADA
   ABAJO, como el renglon donde se escribiria una palabra: en el mockup su centro
   cae al 88% de la altura de la burbuja, no al 50%. De ahi el align-items:
   flex-end de la burbuja y este margen de 8px. */
.level-chat-line {
  display: block; height: 2px; width: 120px;
  background: var(--text-dim); border-radius: 2px;
}

.level-audio-count { font-size: 0.85rem; color: var(--text-dim); }
.level-audio-count.hidden { display: none; }

/* ---- el cruce entre pregunta y pregunta ---- */
/* =====================================================
   XFADE — el cruce entre una ronda y la siguiente

   Un solo componente para SEIS pantallas: el test de nivel y cinco juegos
   (Fill in the Blanks, Word Links, Impostor, Hear It, Emoji Match). Copiarlo
   por juego habria dado seis copias que se separan con el tiempo — es el mismo
   error que costo el bug del arco congelado.

   Medido cuadro a cuadro sobre el video de referencia (30 fps, cuadros 77-88):
   400ms, las dos capas a la vez y SIN delay, curva ease-out y desplazamiento
   vertical CERO. Los 60px del video son el 4,75% de un ancho de app de 1262.
   Cada escenario declara sus px con --xfade-out / --xfade-in, escalados a SU
   ancho con esa misma proporcion (4,75% al salir, 4,44% al entrar).
   ===================================================== */
.xfade-stage {
  position: relative;
  --xfade-out: -43px; /* 4,75% de 900, el contenedor del test */
  --xfade-in: 40px;   /* 4,44% de 900 */
}

/* El clon de la saliente. Fuera del flujo, encima, y sin capturar toques: un
   toque rapido no puede caer en una opcion de la ronda ANTERIOR. */
.xfade-clone {
  position: absolute;
  top: 0; left: 0; width: 100%;
  pointer-events: none;
}

@keyframes xfadeOut {
  from { transform: translateX(0);                 opacity: 1; }
  to   { transform: translateX(var(--xfade-out));  opacity: 0; }
}
@keyframes xfadeIn {
  from { transform: translateX(var(--xfade-in));   opacity: 0; }
  to   { transform: translateX(0);                 opacity: 1; }
}
/* cubic-bezier(0,0,.58,1) es el `ease-out` de CSS: a mitad de tiempo lleva
   recorrido el 68,5% del camino, que es el ancla medida del video (68%). Un
   ease-out cubico daria 87,5% y se veria mucho mas brusco. */
.xfade-clone.xfade-out { animation: xfadeOut 400ms cubic-bezier(0, 0, .58, 1) both; }
.xfade-in              { animation: xfadeIn  400ms cubic-bezier(0, 0, .58, 1) both; }

/* Los px de cada juego, con la misma proporcion (4,78% al salir, 4,44% al
   entrar) sobre el ancho MEDIDO de su envoltorio. La tabla completa esta en el
   reporte; aqui van los anchos de los que salen. */
#imp-stage    { --xfade-out: -30px; --xfade-in: 28px; } /* envoltorio 640 */
#wl-stage     { --xfade-out: -29px; --xfade-in: 28px; } /* envoltorio 620 */
#blanks-stage { --xfade-out: -27px; --xfade-in: 25px; } /* envoltorio 560 */
#hi-stage     { --xfade-out: -27px; --xfade-in: 25px; } /* envoltorio 560 */
#em-stage     { --xfade-out: -27px; --xfade-in: 25px; } /* envoltorio 560 */

/* ---- las cuatro opciones ---- */
/* Fila 7,42% de B = 59; hueco 2,15% = 17. Siempre CUATRO: los 216 ítems del
   banco tienen 4 opciones (el mockup enseña 3 porque estaba a medio dibujar). */
/* 640 y no los 860 del contenedor: el mockup las da al 68,2% de B (543px) y a
   ancho completo se leen como una franja. 640 es el 80,4% de B — entre los dos,
   y por debajo de ~600 las frases enteras de grammar_error empiezan a partirse
   en dos lineas. */
.level-options { display: grid; gap: 17px; width: min(640px, 100%); margin-inline: auto; }
.level-option {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 59px;
  padding: 10px 72px; /* deja sitio al badge a los dos lados: el texto queda
                         centrado en la FILA, no pegado al badge */
  font-size: 1.05rem;
  font-weight: 700;
  text-align: center;
  color: var(--text);
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  background: var(--panel-85);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
/* hover y foco, nunca un color de acierto o error: el test no da feedback a
   propósito */
.level-option:hover,
.level-option:focus-visible { transform: translateY(-1px); box-shadow: 0 0 14px rgba(155, 48, 255, 0.4); }

/* Badge: 7,0% del ancho de la fila, con un inset del 2,0%. Va aria-hidden en
   el markup — el lector de pantalla lee la opción, no «uno». */
.level-option-badge {
  position: absolute;
  left: 16px;
  width: 56px;
  height: 38px;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.05rem; font-weight: 800;
  color: var(--accent-bright);
  border: 1px solid var(--line-soft);
  border-radius: 10px;
}

/* ---- el pop-up de salida ---- */
.level-exit-card { max-width: 420px; text-align: center; }
.level-exit-card h2 { margin-bottom: 8px; }
/* Pequeño a propósito: informa, no asusta. */
.level-exit-note { font-size: 0.88rem; color: var(--text-dim); margin-bottom: 22px; }
.level-exit-actions { display: flex; gap: 14px; justify-content: center; }
.level-exit-btn {
  min-width: 110px;
  padding: 12px 22px;
  font: inherit; font-size: 1rem; font-weight: 800; letter-spacing: 1px;
  color: #fff; cursor: pointer;
  border: none; border-radius: 12px;
  background: var(--accent);
}
.level-exit-btn:hover { background: var(--accent-bright); }
.level-exit-btn:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 2px; }
.level-exit-yes { background: rgba(255, 255, 255, 0.12); }
.level-exit-yes:hover { background: rgba(255, 255, 255, 0.2); }

/* =====================================================
   LEVEL TEST — PANTALLA «MI NIVEL» (#level-mine)
   El estado de #screen-level cuando YA hay un nivel guardado, y TAMBIÉN lo que
   se ve al terminar el test: había una segunda pantalla (#screen-level-result)
   diciendo lo mismo con otro diseño, y se borró. Las dos vistas son este mismo
   panel; la de recién terminado añade .level-mine--result, que solo cambia el
   título y pone VOLVER en el hueco de INFO.

   El arco (.level-gauge*) lo comparte además la barra de portada, que clona
   este mismo nodo. La tarjeta de recomendación (.level-rec-card*) y la cascada
   (.pop-cascade) siguen siendo reutilizables igual.
   ===================================================== */

.level-mine {
  display: none; /* lo enciende lvOpen() según haya nivel o no */
  max-width: 720px;
  margin: 12px auto 60px;
  padding: 26px 24px 30px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 22px;
}
.level-mine.active { display: block; }

/* ---- fila superior: título + botones a la izquierda, arco a la derecha ---- */
.level-mine-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.level-mine-head { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
.level-mine-title { font-size: 2.1rem; font-weight: 900; line-height: 1.1; }
.level-mine-btns { display: flex; gap: 12px; flex-wrap: wrap; }
.level-mine-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 20px;
  font: inherit; font-size: 0.95rem; font-weight: 800; letter-spacing: 1px;
  color: #fff; cursor: pointer;
  border: none; border-radius: 12px;
  background: var(--accent);
  transition: transform 0.12s ease, background 0.12s ease;
}
.level-mine-btn:hover, .level-mine-btn:focus-visible { background: var(--accent-bright); transform: translateY(-1px); }
/* Vista de «recien terminado»: mismo panel, VOLVER en lugar de INFO. Dos
   reglas display:none propias y NADA de .hidden — este proyecto no tiene regla
   generica, asi que un elemento con esa clase se seguiria pintando. */
.level-mine--result #level-info-btn { display: none; }
.level-mine:not(.level-mine--result) #level-back-btn { display: none; }

.level-info-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%;
  border: 1.5px solid currentColor;
  font-size: 0.72rem; font-weight: 900; font-style: italic;
}

/* ---- el arco ---- */
/* El SVG escala solo con el ancho del wrap; el número y el CEFR van encima en
   HTML, así que heredan la tipografía del sitio en vez de la de <text>. */
/* padding-bottom: hueco reservado para el 0 y el 50, que ahora van DEBAJO de
   las puntas del semicirculo en vez de a los lados. Ademas de leerse mejor,
   quita el desborde que tenian: centradas sobre el extremo asomaban media
   palabra fuera del ancho de la caja. */
.level-gauge-wrap { position: relative; width: 230px; flex: none; padding-bottom: 22px; }
.level-gauge { display: block; width: 100%; height: auto; overflow: visible; }
.level-gauge-track,
.level-gauge-fill {
  fill: none;
  stroke-width: 15;
  stroke-linecap: round;
}
.level-gauge-track { stroke: rgba(155, 48, 255, 0.22); }
.level-gauge-fill { stroke: var(--accent-bright); }

/* Anclado por ABAJO, no por arriba: el numero y el CEFR se leen como un grupo
   apoyado en la base del semicirculo, con aire sobre el numero. Anclarlo por
   arriba los dejaba colgando del trazo. El SVG ocupa 200x120 de viewBox y el
   arco baja hasta y=110, o sea el 91,7% del alto del svg. */
.level-gauge-center {
  position: absolute;
  left: 0; right: 0;
  bottom: 30px; /* 22 de padding + margen sobre la linea de base del arco */
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  pointer-events: none;
}
.level-gauge-num { font-size: 3.2rem; font-weight: 900; line-height: 1; }
/* aparece al terminar el llenado, no durante: un CEFR que va cambiando
   mientras sube el número se lee como un error, no como una animación */
.level-gauge-cefr {
  font-size: 1.15rem; font-weight: 800; color: var(--text);
  opacity: 0; transition: opacity 0.25s ease;
}
.level-gauge-wrap.done .level-gauge-cefr { opacity: 1; }

/* Bajo las puntas del arco. Las puntas caen en x=20 y x=180 de un viewBox de
   200, o sea al 10% y al 90% del ancho de la caja; translateX(-50%) las centra
   sobre su punta sin salirse, porque ahora sobra un 10% a cada lado. */
.level-gauge-mark {
  position: absolute; bottom: 0;
  transform: translateX(-50%);
  font-size: 0.82rem; font-weight: 700; color: var(--text-dim);
}
.level-gauge-min { left: 10%; }
.level-gauge-max { left: 90%; }

/* Variante de la barra de portada: sin las marcas 0 y 50, y sin el
   padding-bottom que existia solo para hacerles sitio. El resto del componente
   —trazo, relleno, numero, CEFR— es exactamente el mismo. */
.level-gauge-wrap--bare { padding-bottom: 0; }
.level-gauge-wrap--bare .level-gauge-mark { display: none; }
.level-gauge-wrap--bare .level-gauge-center { bottom: 4%; }
.level-gauge-wrap--bare .level-gauge-num { font-size: 1.45rem; }
.level-gauge-wrap--bare .level-gauge-cefr { font-size: 0.72rem; }
/* Sin nivel todavia: el carril gris solo, «…» en vez del numero y sin CEFR.
   El relleno se queda a offset completo desde lvPaintGauge, asi que aqui no
   hay que apagar nada mas. */
.level-gauge-wrap[data-state="empty"] .level-gauge-cefr { display: none; }
.level-gauge-wrap[data-state="empty"] .level-gauge-num { color: var(--text-dim); }

/* ---- las tres destrezas ---- */
.level-skills-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin: 34px 0 0;
}
.level-skill-cell { min-width: 0; text-align: center; }
.level-skill-name {
  font-size: 0.95rem; color: var(--text-dim); margin-bottom: 8px;
}
.level-skill-box {
  display: flex; align-items: center; justify-content: center;
  min-height: 62px;
  padding: 12px 8px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line-soft);
}
/* Etiqueta, nunca número ni CEFR: con 8 ítems por destreza el error ronda los
   ±7 puntos, casi banda y media, y un decimal ahí promete una precisión que no
   existe y fluctúa entre intentos sin que el jugador haya cambiado. */
.level-skill-value { font-size: 1.25rem; font-weight: 800; }
.level-skill-cell[data-label="basico"] .level-skill-value { color: #7ee2a0; }
.level-skill-cell[data-label="intermedio"] .level-skill-value { color: #f0d47a; }
.level-skill-cell[data-label="avanzado"] .level-skill-value { color: var(--accent-bright); }
.level-skill-note {
  display: none;
  margin-top: 7px;
  font-size: 0.72rem; line-height: 1.35; color: var(--text-dim);
}
.level-skill-cell.not-measured .level-skill-note { display: block; }
.level-skill-cell.not-measured .level-skill-value { color: var(--text-dim); }

/* ---- recomendaciones ---- */
.level-recs-block { margin-top: 38px; }
.level-mine-recs-title { font-size: 1.55rem; font-weight: 900; line-height: 1.15; }
.level-mine-recs-sub { font-size: 0.95rem; color: var(--text-dim); margin-top: 4px; }

/* Mismo reparto que .card-grid — flex, wrap, centrado — porque la tarjeta es un
   CLON de la .game-card real y trae su propio `width: 230px` (160px en <=480).
   Lo unico que cambia respecto a la portada es la separacion y el ancho del
   contenedor: las tarjetas no se encogen ni se estiran, solo se colocan. */
.level-rec-cards {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 24px;
  margin-top: 20px;
}
/* Envoltorio, no tarjeta: la tarjeta es el clon que va dentro. Es un <div> y no
   un <button> a proposito — el clon YA es un <button>, y un boton dentro de
   otro es HTML invalido que el navegador desanida. */
.level-rec-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}
/* El clon hereda su tamano, su borde, su hover y su footer de .game-card. No se
   redefine ninguno: si retocas la tarjeta de la portada, esta se entera sola. */
.level-rec-card > .game-card { cursor: pointer; }
.level-rec-diff {
  min-height: 18px;
  font-size: 0.86rem; font-weight: 700; text-align: center;
  color: var(--text-dim);
}
/* Bomb Word no tiene selector, asi que aqui no va nada: inventarle una etiqueta
   nombraria un ajuste que el juego no usa. El min-height mantiene alineadas las
   tarjetas de la fila aunque una vaya sin texto. */

/* ---- modal de INFO ---- */
.level-info-card { max-width: 460px; text-align: left; }
.level-info-card h2 { margin-bottom: 14px; }
.level-info-card .intro-desc + .intro-desc { margin-top: 12px; }

/* ---- la cascada de entrada ---- */
/* Equivalente a endPop, con los mismos tiempos y curva, pero regla propia:
   .end-panel > * está calibrada para 6 hijos y esta pantalla tiene más
   secciones. Retocar una nunca debe mover la otra. */
@keyframes popCascade {
  from { opacity: 0; transform: translateY(10px) scale(0.85); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
/* El escalonado de 60ms lo pone runPopCascade() en linea, contando SOLO los
   hijos que participan: con :nth-child un hijo en display:none se lleva igual
   su turno y deja un hueco mudo en mitad de la cascada. La .intro-card es el
   caso extremo — 16 hijos y solo 5 o 6 visibles por juego. Aqui solo va la
   animacion. */
.pop-cascade.run > * { animation: popCascade 0.35s ease both; }
/* .pop-skip queda FUERA de la cascada, y runPopCascade tampoco le da turno.
   Dos motivos distintos para lo mismo:
   - Los avisos llegan de forma asincrona (el de voces espera a
     hiWhenVoicesReady), asi que cuando se reparten los retardos todavia estan
     en display:none y entrarian con retardo 0, a la vez que la primera seccion.
   - La ✕ y el selector de idioma del modal son position:absolute en su esquina:
     animarlos con translateY+scale los haria entrar desplazados, y ademas son
     controles, no contenido. */
.pop-cascade.run > .pop-skip { animation: none; }

/* Con la preferencia activada: ni cascada, ni conteo, ni llenado. El JS lee
   esta misma media query para pintar el arco lleno y el número final de una. */
@media (prefers-reduced-motion: reduce) {
  .pop-cascade.run > * { animation: none; }
  .level-gauge-cefr { transition: none; }
}

@media (max-width: 768px) {
  .level-entry { width: calc(100% - 24px); padding: 6px 16px; }
  .level-bar-img { max-width: 210px; }
  .level-bar-gauge .level-gauge-wrap { width: 96px; }
  /* ---- la pantalla de pregunta en tablet ---- */
  .level-test-header { gap: 16px; padding: 16px 16px 0; }
  .level-progress { height: 16px; }
  .level-test-wrap { padding: 0 16px 36px; }
  .level-test-title { font-size: 2rem; margin: 24px 0 18px; }
  /* the zone shrinks with the type, but keeps absorbing the tallest item so
     the option buttons still never move */
  .level-prompt-zone { min-height: 180px; margin-bottom: 20px; } /* medido: el chat */
  .level-prompt { font-size: 1.2rem; }
  .level-q-sub { font-size: 1.2rem; }
  .level-option { min-height: 54px; padding: 10px 64px; }
  .level-option-badge { width: 48px; height: 34px; left: 12px; }
  .level-options { gap: 14px; }

  /* ---- «mi nivel» en tablet ---- */
  .level-mine { padding: 22px 18px 26px; margin-bottom: 48px; }
  .level-mine-title { font-size: 1.8rem; }
  .level-gauge-wrap { width: 200px; }
  .level-gauge-num { font-size: 2.8rem; }
  .level-mine-recs-title { font-size: 1.35rem; }
}

@media (max-width: 480px) {
  /* Imagen y arco SIGUEN en fila; nada de apilar. El arco mengua primero
     porque es lo que menos informacion pierde al encoger. */
  .level-entry { padding: 6px 14px; gap: 12px; }
  .level-bar-img { max-width: 220px; }
  .level-bar-gauge .level-gauge-wrap { width: 84px; }
  .level-gauge-wrap--bare .level-gauge-num { font-size: 1.15rem; }
  .level-gauge-wrap--bare .level-gauge-cefr { font-size: 0.62rem; }
  .level-intro { padding: 22px 16px; }
  .level-title { font-size: 1.25rem; }
  .level-desc { font-size: 0.88rem; }
  /* ---- la pantalla de pregunta en telefono ---- */
  /* Todo se comprime menos la zona de contenido, que sigue siendo la que
     impide que las opciones se muevan entre preguntas. */
  .level-test-header { gap: 12px; padding: 12px 12px 0; }
  .level-close-btn { width: 40px; height: 40px; }
  .level-close-btn svg { width: 18px; height: 18px; }
  .level-progress { height: 14px; }
  .level-test-wrap { padding: 0 12px 28px; }
  .level-test-title { font-size: 1.6rem; margin: 18px 0 14px; }
  .level-prompt-zone { min-height: 144px; margin-bottom: 20px; }
  .level-prompt { font-size: 1.02rem; }
  .level-q-sub { font-size: 1.02rem; }
  .level-speaker-btn { width: 96px; height: 68px; }
  .level-chat-avatar { width: 46px; height: 46px; }
  .level-chat-bubble { min-width: 150px; min-height: 52px; padding: 8px 10px 6px; gap: 8px; }
  .level-chat-speaker { width: 36px; height: 30px; }
  .level-chat-line { width: 70px; }
  .level-options { gap: 12px; }
  .level-option { min-height: 52px; padding: 10px 52px; font-size: 0.94rem; }
  .level-option-badge { width: 40px; height: 30px; left: 8px; font-size: 0.92rem; }
  .level-exit-btn { min-width: 92px; padding: 11px 16px; }
  /* 4,75% de 360 serian 17px, pero por debajo de ~18 el deslizamiento se lee
     como un temblor y no como una direccion. 20px siguen siendo el 5,6%. */
  /* 4,75% de 360 serian 17px, pero por debajo de ~18 el deslizamiento se lee
     como un temblor y no como una direccion. 20px siguen siendo el 5,6%.
     Con variables basta redefinirlas: los @keyframes no se tocan. */
  .xfade-stage { --xfade-out: -20px; --xfade-in: 19px; }
  /* En los cinco juegos el envoltorio ocupa casi todo el ancho del telefono, asi
     que +19 dejaba el borde derecho a -0,2px del viewport: sin scrollbar, pero
     por redondeo, no por holgura. 14 da margen de verdad. El test se queda en
     19 porque su contenedor esta mas centrado. */
  #wl-stage, #imp-stage, #blanks-stage, #hi-stage, #em-stage {
    --xfade-out: -18px; --xfade-in: 14px;
  }

  /* ---- «mi nivel» en teléfono ---- */
  /* El mockup pone arco y botones lado a lado; a 360px no caben. Se recompone
     en vertical: título, arco centrado, y los dos botones en una fila de dos a
     ancho completo, que es donde el pulgar los alcanza. */
  .level-mine { padding: 18px 14px 22px; margin-bottom: 40px; border-radius: 18px; }
  .level-mine-top { flex-direction: column-reverse; align-items: stretch; gap: 14px; }
  .level-mine-head { gap: 14px; }
  .level-mine-title { font-size: 1.5rem; text-align: center; }
  .level-mine-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .level-mine-btn { justify-content: center; padding: 12px 8px; font-size: 0.88rem; }
  .level-gauge-wrap { width: min(230px, 100%); margin: 0 auto; }
  .level-gauge-num { font-size: 2.6rem; }
  .level-gauge-cefr { font-size: 1rem; }

  .level-skills-row { gap: 8px; margin-top: 26px; }
  .level-skill-name { font-size: 0.8rem; }
  .level-skill-box { min-height: 54px; padding: 10px 4px; }
  .level-skill-value { font-size: 0.9rem; }
  .level-skill-note { font-size: 0.66rem; }

  .level-recs-block { margin-top: 30px; }
  .level-mine-recs-title { font-size: 1.25rem; }
  .level-mine-recs-sub { font-size: 0.86rem; }
  /* Una por fila, igual que .card-grid a este ancho. El clon ya mide 160px
     porque .game-card lo declara aqui; no hace falta tocar su tamano. */
  .level-rec-cards { gap: 18px; }
}

/* =====================================================
   WORD LAB RADIO
   Historias reales narradas en ingles: fragmento de audio, pregunta,
   fragmento, pregunta... hasta el resultado.

   Reutiliza sin copiar: .level-test-header / .level-close-btn /
   .level-progress (la cabecera del test de nivel), .end-panel + endPop
   (el panel final de los trece juegos), .st-wordlist / .st-word-meaning
   (las palabras pulsables de Strands) y #level-exit-overlay (el pop-up
   de salida). Lo unico propio es lo que va aqui debajo.

   🚨 Este proyecto NO tiene una regla generica `.hidden { display: none }`.
   Cada componente nuevo que se apague con .hidden necesita SU regla, y son
   las agrupadas al final de este bloque. Sin ellas la clase se pone, no da
   error, y el elemento se sigue pintando.
   ===================================================== */

/* ---- la tarjeta 14 de la portada ---- */
/* Ya es una tarjeta con imagen (RADIO.webp), como las otras trece: el patron
   .card-art-asset + .card-art-img, y el --art-scale de mas arriba.

   🚨 `.mini-radio` y sus tres piezas NO SON CODIGO MUERTO aunque la tarjeta ya
   no las use. Las usa rdRenderList() (app.js) como dibujo de reserva de las
   historias de la radio que todavia no traen `cover`. Si se borran, la lista
   de historias se queda con tarjetas vacias en cuanto Omar anada una historia
   sin portada. Lo mismo vale para `.rd-story-art .mini-radio` de mas abajo.

   El degradado propio que habia aqui (#45195e, mas claro que el generico) se
   quito: existia para que la mini-radio dibujada se leyera encima, y con una
   imagen opaca que llena la caja esa razon desaparece. Ahora las catorce
   comparten el fondo de .card-art. */
.mini-radio { position: relative; display: block; width: 108px; height: 76px; }
.mini-radio-body {
  position: absolute; inset: auto 0 0 0;
  height: 62px;
  display: flex; align-items: center; justify-content: center;
  background: #6f6f78;
  border-radius: 10px;
  box-shadow: inset 0 -6px 0 rgba(0, 0, 0, 0.22);
}
.mini-radio-dial {
  display: flex; align-items: flex-end; gap: 5px;
  width: 62px; height: 30px;
  padding: 4px 6px;
  background: #f2f2f4;
  border-radius: 4px;
}
.mini-radio-dial i { flex: 1; background: #6f6f78; border-radius: 1px; height: 60%; }
.mini-radio-dial i:nth-child(2) { height: 100%; background: #d13b3b; }
.mini-radio-dial i:nth-child(3) { height: 78%; }
.mini-radio-antenna {
  position: absolute; top: 0; right: 16px;
  width: 4px; height: 26px;
  background: #8a8a93;
  border-radius: 2px;
  transform: rotate(18deg);
  transform-origin: bottom center;
}

/* ---- la lista de historias ---- */
.rd-list-title {
  max-width: 900px;
  margin: 6px auto 8px;
  padding: 0 20px;
  font-size: 2rem;
  font-weight: 900;
  letter-spacing: 2px;
  text-align: center;
}
.rd-list-desc {
  max-width: 640px;
  margin: 0 auto 26px;
  padding: 0 20px;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
  text-align: center;
}
.rd-list-state {
  max-width: 640px;
  margin: 0 auto 26px;
  padding: 0 20px;
  color: var(--text-dim);
  text-align: center;
}

/* Rejilla preparada para veinte historias, no para una: auto-fill con un
   minimo, asi que la vigesima se reparte sola sin tocar nada. */
/* 🚨 LOS TRES VALORES VAN JUNTOS. La proporcion hueco/portada que quiere Omar
   es 0,41 (medida de su mockup); antes era 0,08. Se consigue con portadas mas
   PEQUENAS y huecos mucho mas GRANDES, y hace falta bajar las tres cosas a la
   vez: si se deja el `max-width: 900px`, el grid reparte esos 900 entre las
   columnas y las portadas vuelven a crecer aunque el `gap` sea enorme.
   Medido con estos valores en 1100 de ancho: 3 columnas, portada de 163 px,
   hueco horizontal 66 y vertical 63 -> 0,406.
   `minmax(140px, …)` con auto-fill es lo que decide CUANTAS columnas caben; a
   660 salen tres. Si en algun sitio salieran cuatro y se apretara, se mide
   antes de tocar el numero. */
.rd-story-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 56px 66px;
  max-width: 660px;
  margin: 0 auto;
  padding: 0 20px 40px;
}
/* Desde el 2026-09-12 la tarjeta no es una caja: es la portada con su sombra y
   el titulo debajo, al aire. Por eso pierde fondo, borde y el texto se centra.
   🚨 `overflow: visible` NO es cosmetico: con `hidden` la tarjeta RECORTA la
   sombra de .rd-story-art y el cambio entero no se ve. El `overflow: hidden`
   de .rd-story-art si se queda — ese es el que recorta la imagen contra las
   esquinas redondeadas. */
.rd-story-card {
  display: flex;
  flex-direction: column;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 14px;
  color: var(--text);
  text-align: center;
  cursor: pointer;
  overflow: visible;
  transition: transform 0.15s ease;
}
/* El halo se MUDO a .rd-story-art y va SUMADO a la sombra dura, no en lugar de
   ella: `box-shadow` no se acumula entre reglas, asi que dejar aqui solo el
   halo haria que al pasar el raton la tarjeta PERDIERA su sombra de abajo —
   el mismo fallo que ya se corrigio en las tarjetas de la portada.
   Y se muda en vez de quitarse porque el :focus-visible es la unica senal que
   tiene quien navega con teclado. */
.rd-story-card:hover,
.rd-story-card:focus-visible { transform: translateY(-3px); }
.rd-story-card:hover .rd-story-art,
.rd-story-card:focus-visible .rd-story-art {
  box-shadow: 0 5px 0 rgba(0, 0, 0, 0.55),
              0 0 18px rgba(155, 48, 255, 0.45);
}
/* 🚨 CUADRADA, no los 190px de alto de las tarjetas de juego: es lo que
   distingue una historia de un juego de un vistazo. */
.rd-story-art {
  position: relative;
  aspect-ratio: 1 / 1;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  background: var(--panel);
  border-radius: 14px;
  /* Sombra DURA, sin difuminar y solo hacia abajo — lo que Omar llama «estilo
     trazo». Si alguna vez quiere otra cosa, estas son las que se probaron:
       0 4px 0 #0B1114                                      (mas opaca)
       0 5px 0 rgba(0,0,0,.45), 0 8px 10px rgba(0,0,0,.35)  (+ difuminado)
       0 8px 14px rgba(0,0,0,.55)                           (solo difusa)     */
  box-shadow: 0 5px 0 rgba(0, 0, 0, 0.55);
  transition: box-shadow 0.15s ease;
}
.rd-story-art img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Portada con la colita dibujada: el ancho es el que manda en esta caja
   cuadrada, asi que anclar arriba recorta EXACTAMENTE la cola y deja la tarjeta
   entera. Centrado se comeria 7,6 px del borde de arriba y otros tantos de la
   cola, que es lo peor de los dos mundos. Lo pone rdRenderList(). */
.rd-story-art-tail { object-position: top; }
.rd-story-art .mini-radio { transform: scale(1.15); }
.rd-story-footer { padding: 10px 0 0; background: transparent; }
.rd-story-name { display: block; font-size: 0.85rem; font-weight: 800; line-height: 1.25; }
/* 🚨 ESTAS DOS REGLAS NO TIENEN NODO desde el 2026-09-12: rdRenderList() dejo
   de crear los <span> de los minutos+nivel y del mejor marcador, porque la
   lista pasó a ser solo portada y titulo. NO se borran a proposito — Omar puede
   querer el marcador de vuelta, y entonces esto ya esta. Sus cadenas
   `story_meta` y `story_best` siguen igualmente en los dos ui_strings.
   ⚠️ Y que no se creen NO es lo mismo que ocultarlos: un `display:none` los
   dejaria en el DOM y un lector de pantalla los seguiria leyendo. */
.rd-story-meta { display: block; margin-top: 6px; color: var(--text-dim); font-size: 0.78rem; letter-spacing: 0.5px; }
.rd-story-best { display: block; margin-top: 4px; color: var(--accent-bright); font-size: 0.78rem; font-weight: 700; }

/* ---- el reproductor ---- */
/* La pregunta se monta ENCIMA de la escena (mockup), asi que la pantalla es
   el contexto de posicionamiento y el panel va absolute contra ella. */
#screen-radio-play.active { position: relative; display: flex; flex-direction: column; min-height: 100vh; }
.rd-stage {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* 20 y no 26: es aire, y con la pregunta abierta hay DOS gaps, asi que
     bajarlo compra 12px para la escena, que es lo que se quiere ver grande. */
  gap: 20px;
  padding: 10px 20px 24px;
}
/* 21% del ancho de la pantalla, como el mockup, pero acotado: a 390px de
   viewport el 21% son 82px de escena y no se lee. El clamp deja mandar al 21%
   en escritorio y pone un suelo para telefono; las media queries de abajo
   suben ese suelo. La caja conserva la proporcion del propio Lottie
   (628x852) para que el SVG no se deforme. */
/* 🚨 aspect-ratio = EL LIENZO DE radio_scene.json, no un numero de diseño.
   El SVG del Lottie se dibuja con `xMidYMid meet`: si esta caja tiene otra
   proporcion, el arte se encoge y queda centrado con banda muerta, y entonces
   los porcentajes de .rd-bubble —que estan calculados sobre el lienzo— dejan
   de caer donde el dibujo. Paso de 628/852 (lienzo viejo) a 562/706 con la
   reexportacion: medido, con el valor viejo la burbuja se iba 12,8 px arriba y
   sobraba 8,6 px de alto. Si Omar reexporta, esto se actualiza tambien. */
/* 🚨 MANDA EL ALTO, no el ancho. Antes era `width: clamp(190px, 21vw, 320px)`
   y ese tope de 320 era el que la hacia pequeña en todas partes: la escena se
   medía en vw cuando lo que de verdad la limita es el ALTO de la ventana —en
   una pantalla ancha y baja el 21vw daba una escena que no cabia, y por eso
   habia un tope. Con la altura al mando el tope sobra y el ancho lo deriva el
   aspect-ratio.

   El presupuesto vertical, medido (cabecera + padding + escena + gap + boton,
   y con la pregunta abierta ademas gap + contador de repeticiones):

     1440x780   escena <= 515px      1280x680   escena <= 423px  <- el que aprieta
     390x700    <= 514px de alto, pero la capa el ancho

   60vh deja margen sobre el caso apretado, y por eso las media queries de
   abajo ya no tocan el tamaño de la escena.

   🚨 El limite de ancho va DENTRO del alto, no en un `max-width`. Con
   `max-width` el navegador resuelve primero el alto (60vh), saca el ancho del
   aspect-ratio, y luego lo recorta — pero el alto NO vuelve atras, asi que la
   caja se achata y el aspect-ratio deja de cumplirse. Medido a 360x740: salia
   309,6 x 444, o sea 0,697 en vez de 0,796. Y eso reintroduce la banda muerta
   del Lottie y descuadra los porcentajes de .rd-bubble, que es el bug que ya
   costo una tarea entera.
   `108vw` es el 86% del ancho traducido a alto: 0,86 / 0,796. */
.rd-scene {
  position: relative;
  height: clamp(240px, min(60vh, 108vw), 520px);
  width: auto;
  aspect-ratio: 562 / 706;
}
.rd-scene-anim { position: absolute; inset: 0; }
.rd-scene-anim svg { display: block; width: 100%; height: 100%; }
/* El hueco del bocadillo, en % de la caja de la escena, sobre el bocadillo
   que dibuja radio_scene.json. Si Omar lo mueve en el After Effects, estos
   cuatro numeros son lo unico que hay que retocar. */
/* 🚨 ESTOS CINCO NUMEROS SE RECALCULAN SI OMAR REEXPORTA radio_scene.json.
   No son de diseño: describen donde cae UN rectangulo concreto del Lottie.

   La burbuja son dos capas dentro de `Pre-comp 3` -> comp_0, las dos con el
   mismo rect de 344,917 y radio 25:
     Shape Layer 2  el rect MORADO OSCURO rgb(48,0,64) — es el hueco. Es la que
                    lleva la animacion de escala, y sobre ella va la portada.
     Shape Layer 1  el mismo rect al 106,054%, rgb(120,0,178), parentado a la
                    anterior y dibujado detras: eso es el borde morado.

   Cadena hasta el lienzo de 562x706, para el rect oscuro en su fotograma final:
     grupo Rectangle 1  tr.p = (-44,503, -226,273)     <-- no olvidar este
     capa Shape Layer 2 p=(952,585, 403,028) a=(-48,419, -29,191) s=76,618%
     capa Pre-comp 3    p=(281, 377) a=(960, 540) s=78,125%
   da left 174,32 · top 48,78 · lado 206,46 · radio 14,97, o sea:            */
.rd-bubble {
  position: absolute;
  left: 31.02%;  top: 6.91%;          /* 174,32/562 y 48,78/706              */
  width: 36.74%; height: 29.24%;      /* 206,46/562 y 206,46/706 (es cuadrado) */
  /* en % y NO en px: tiene que escalar con la escena, que va de 174px de lado
     en telefono a 320 en escritorio. 14,97/206,46 = 7,25% */
  border-radius: 7.25%;
  overflow: hidden;
  /* El punto fijo del escalado es el ANCLA de Shape Layer 2, no su centro:
     a=(-48,419,-29,191) cae en el lienzo en (275,21, 269,99), que respecto a
     la caja de arriba es (275,21-174,32)/206,46 y (269,99-48,78)/206,46.
     El 107,1% vertical esta POR DEBAJO del borde inferior: por eso la burbuja
     "sale de abajo" en vez de crecer desde su centro. */
  transform-origin: 48.9% 107.1%;
  /* 🚨 EL TAMAÑO LO ESCRIBE JS, no hay animacion CSS aqui.
     Arranca en 0 para que la portada no se vea a tamaño completo antes del
     primer fotograma de la escena; a partir de ahi rdBubbleFollow() (app.js)
     le escribe el `transform` en cada `enterFrame` del Lottie.

     Hubo una animacion CSS con estos mismos keyframes y estaba bien calculada
     —las curvas coincidian con el Lottie dentro de 1,5 px— pero eran DOS
     RELOJES: el del navegador y el de lottie-web. Medido, UN SOLO fotograma de
     desfase (16 ms) abria 26,5 px entre la portada y el borde morado, porque en
     el arranque la burbuja crece a 1,6 px por milisegundo. Y el desfase no es
     una constante que se pueda calibrar: depende de lo que tarde lottie en
     construir el SVG, del navegador y del telefono.
     Leyendo el fotograma del propio Lottie la sincronia es por construccion: si
     el Lottie tartamudea, la portada tartamudea igual. */
  transform: scale(0);
}
.rd-bubble img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ---- portadas que traen SU PROPIA colita dibujada ---- */
/* Se enciende desde el banco (`cover_tail`) y la pone rdStart(). La portada deja
   de ser cuadrada: los primeros N px de alto son la tarjeta —esa si cuadrada, y
   es la que llena esta caja— y lo de abajo es la colita del bocadillo dibujada
   en After Effects.

   Por eso `width: 100%; height: auto` en vez del `object-fit: cover`: con la
   tarjeta recortada a cuadrada en el propio archivo, el ancho manda y la parte
   de la tarjeta cae EXACTA sobre la caja sin un solo numero magico. Lo que
   sobra por abajo es la cola, y tiene que salirse.

   🚨 `overflow: hidden` la cortaria, y no vale ponerlo en `visible` porque
   entonces tambien escaparia el pelo horizontal. `clip-path` recorta arriba y
   a los lados igual que antes —con el mismo radio— y abre SOLO por abajo:
   -12% de la caja da margen de sobra para el 5,7% que mide la cola. */
.rd-bubble-tail {
  overflow: visible;
  clip-path: inset(0 0 -12% 0 round 7.25%);
}
.rd-bubble-tail img { width: 100%; height: auto; object-fit: fill; }

.rd-controls { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.rd-play-btn {
  display: block;
  width: clamp(150px, 17vw, 230px);
  padding: 0;
  background: none;
  border: none;
  border-radius: 14px;
  cursor: pointer;
}
.rd-play-btn:disabled { cursor: not-allowed; opacity: 0.45; }
.rd-play-btn:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 4px; }
.rd-play-anim { display: block; aspect-ratio: 1511 / 635; }
.rd-play-anim svg { display: block; width: 100%; height: 100%; }
.rd-replays { color: var(--text-dim); font-size: 0.85rem; letter-spacing: 0.5px; }

/* ---- la pregunta ---- */
.rd-q-panel {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: min(1080px, 92%);
  padding: 26px 26px 30px;
  background: var(--panel-97);
  border: 1px solid var(--panel-border);
  border-radius: 22px 22px 0 0;
  box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.45);
  animation: rdPanelUp 0.32s ease both;
}
@keyframes rdPanelUp {
  from { opacity: 0; transform: translate(-50%, 26px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.rd-q-prompt {
  margin-bottom: 18px;
  font-size: 1.7rem;
  font-weight: 800;
  line-height: 1.2;
  text-align: center;
}
.rd-q-prompt strong { color: var(--accent-soft); font-weight: 900; }
.rd-q-note { margin: -10px 0 16px; color: var(--text-dim); font-size: 0.88rem; text-align: center; }

/* 2x2 en escritorio, como el mockup. En telefono se apilan (media queries de
   abajo): a 2x2 no cabe «They were looking for the wrong kind of person». */
.rd-options { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.rd-option {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 74px;
  padding: 12px 16px 12px 74px;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  color: var(--text);
  background: none;
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  cursor: pointer;
  /* 🚨 `opacity` TIENE que estar en esta lista: es lo que hace que la opcion
     fallada se esfume en vez de desaparecer de golpe (ver .rd-option.gone).
     Los tiempos son los de .spot-sentence, no parecidos: 0,15 s el color y
     0,3 s la opacidad. Son dos juegos de la misma web y el gesto es el mismo. */
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease,
              border-color 0.15s ease, opacity 0.3s ease;
}
.rd-option:hover,
.rd-option:focus-visible { transform: translateY(-1px); box-shadow: 0 0 14px rgba(155, 48, 255, 0.4); }
.rd-option-badge {
  position: absolute;
  left: 16px;
  width: 48px;
  height: 38px;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.05rem; font-weight: 800;
  color: var(--accent-bright);
  border: 1px solid var(--line-soft);
  border-radius: 10px;
}
/* Acierto: verde, y ahi se queda mientras la historia sigue. */
.rd-option.right {
  background: rgba(83, 141, 78, 0.55);
  border-color: #7fc178;
  color: #fff;
}
.rd-option.right .rd-option-badge { color: #d8f5d4; border-color: rgba(216, 245, 212, 0.6); }
.rd-option:disabled { cursor: default; }
.rd-option:disabled:hover { transform: none; box-shadow: none; }

/* Fallar apaga la opcion y DEJA SU SITIO VACIO (mecanica de Spot the Error).

   🚨 `opacity` y NO `display:none`, que es lo que habia. display:none la saca
   de la rejilla, las tres que quedan se recolocan para llenar el hueco y toda
   la pantalla se mueve debajo del dedo justo despues de fallar: castiga dos
   veces. Con opacity la caja sigue ocupando su celda y nada se mueve — medido,
   los cuatro numeros del getBoundingClientRect() de las otras opciones salen
   identicos antes y despues.

   Copiado de .spot-sentence, incluido el rojo: el color entra en 0,15 s y la
   opacidad se va en 0,3 s, asi que se ve un destello y luego el hueco.

   `pointer-events: none` corta el raton; el `btn.disabled` de rdAnswer() la
   saca del tabulador; y el atajo 1-4 la filtra por `:not(.gone)`. Las tres
   vias, porque la opcion sigue AHI: invisible, pero presente. */
.rd-option.gone {
  background: #8d3030;
  border-color: #8d3030;
  opacity: 0;
  pointer-events: none;
}

/* ---- las reglas de .hidden de este bloque ---- */
/* Sin esto la clase no apaga NADA: no hay regla generica en el proyecto. */
.rd-list-state.hidden,
.rd-bubble.hidden,
.rd-replays.hidden,
.rd-q-note.hidden,
.rd-q-panel.hidden { display: none; }

/* =====================================================
   THE INTERVIEW
   =====================================================
   🚨 LAS CLASES SON PROPIAS, COPIADAS DE LAS .rd-* DE LA RADIO, NO
   COMPARTIDAS. Es duplicacion a proposito: las dos pantallas se parecen hoy,
   pero en cuanto una cambie la otra no puede enterarse sin avisar. Compartir
   .rd-story-card entre las dos habria sido mas corto y habria roto la radio el
   dia que la entrevista pidiera otra cosa. */
.iv-list-title {
  max-width: 900px;
  margin: 6px auto 8px;
  padding: 0 20px;
  font-size: 2rem;
  font-weight: 900;
  letter-spacing: 2px;
  text-align: center;
}
.iv-list-desc {
  max-width: 640px;
  margin: 0 auto 26px;
  padding: 0 20px;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
  text-align: center;
}
.iv-list-state {
  max-width: 640px;
  margin: 0 auto 26px;
  padding: 0 20px;
  color: var(--text-dim);
  text-align: center;
}

/* La lista. Las entrevistas todavia no tienen portada, asi que la tarjeta es
   titulo + nivel: por eso esta rejilla NO copia el minmax de 140px de la radio
   —que esta calculado para una imagen cuadrada— sino uno mas ancho, que es lo
   que pide una linea de texto. */
.iv-list-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
  max-width: 660px;
  margin: 0 auto;
  padding: 0 20px 40px;
}
.iv-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 22px 18px;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 14px;
  color: var(--text);
  text-align: center;
  cursor: pointer;
  box-shadow: 0 5px 0 rgba(0, 0, 0, 0.55);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.iv-card:hover,
.iv-card:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 5px 0 rgba(0, 0, 0, 0.55),
              0 0 18px rgba(155, 48, 255, 0.45);
}
.iv-card-name { display: block; font-size: 1.05rem; font-weight: 800; line-height: 1.25; }
.iv-card-meta { display: block; color: var(--text-dim); font-size: 0.78rem; letter-spacing: 0.5px; }
.iv-card-best { display: block; color: var(--accent-bright); font-size: 0.78rem; font-weight: 700; }

/* ---- el reproductor: un CHAT (2026-09-15) ----
   Sustituye a .iv-stage / .iv-controls / .iv-play-btn, que se BORRARON: el
   boton suelto ya no existe y cada pregunta trae el suyo dentro de la burbuja.

   🚨 LOS 8 COLORES DEL MOCKUP VIVEN AQUI, EN LA PANTALLA, Y NO EN UN :root.
   Ya paso con --sp-accent: estaba en un SEGUNDO :root y pintaba en silencio
   cuatro mini-previsualizaciones de la portada. Declarados en la pantalla, no
   pueden alcanzar nada fuera de ella. (Y read_palette() de build_og_image.py
   ni los ve.)
   ⚠️ El fondo del mockup es #1E262B; manda el de la web, --bg-1. */
#screen-interview-play {
  --iv-ink: #c6dde9;       /* avatar y cuadrado del boton (van en los WebP); foco */
  --iv-bubble-bd: #768991; /* borde de la burbuja del entrevistador */
  --iv-bar-fill: #d5fffe;  /* barra llena */
  --iv-bar-track: #37464d; /* carril de la barra */
  --iv-ok-bd: #7dc653;     /* borde verde */
  --iv-ok-bg: #25312d;     /* relleno verde */
  --iv-bad-bd: #b24242;    /* borde rojo */
  --iv-bad-bg: #1d1c21;    /* relleno rojo */
  /* Cuanto se desliza cada fila al entrar. 22px es el punto de partida: en el
     video de Omar son 26px sobre un lienzo de 1920. Se ajusta SOLO aqui. */
  --iv-slide: 22px;
}

/* 🚨 ALTO FIJO, no min-height. El panel de abajo va absolute contra esta
   pantalla (como .rd-q-panel contra #screen-radio-play): con min-height:100vh
   el chat haria CRECER la pantalla y el panel se iria al final del documento.
   Con 100dvh la pantalla es el viewport, y quien hace scroll es el chat.
   100vh primero, de respaldo para navegadores sin dvh.
   #iv-end NO deberia verse afectado: es .overlay, position:fixed, y ningun
   antecesor lleva transform ni filter que le cambie el bloque contenedor —
   medido en el reporte. */
#screen-interview-play.active {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}
#screen-interview-play .level-test-header { flex: none; }

.iv-chat {
  /* 🚨 position:relative NO es decorativo: hace que .iv-chat sea el
     offsetParent de sus mensajes. Sin el, msg.offsetTop se mide contra la
     PANTALLA y suma el alto de la cabecera (64 px a 1024): el ancla del 56%
     caeria al 65%. */
  position: relative;
  flex: 1 1 auto;
  min-height: 0; /* sin esto un hijo flex no encoge y el overflow no aparece */
  overflow-y: auto;
  /* la fila entra desplazada --iv-slide: sin esto saca una barra horizontal */
  overflow-x: hidden;
  /* 🚨 NI se te ocurra poner aqui `scroll-behavior: smooth`: el scroll del ancla
     lo anima ivAnchor() a mano, y con smooth cada paso de su animacion
     arrancaria otra animacion del navegador encima. */
  overscroll-behavior: contain;
  /* 🚨 LA BARRA DE SCROLL, ESCONDIDA — que no es lo mismo que quitar el scroll:
     con rueda, con el dedo y con las flechas (el foco vive dentro del chat)
     sigue desplazandose igual. */
  scrollbar-width: none;        /* Firefox */
  -ms-overflow-style: none;     /* Edge/IE viejos */
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 22px 20px 16px;
}
.iv-chat::-webkit-scrollbar { display: none; } /* Chrome, Safari */

/* 🚨 EL HUECO DE ARRIBA ES UN MARGEN, NO RELLENO (2026-09-16). Como relleno
   CONTABA COMO CONTENIDO, asi que el chat siempre desbordaba y siempre habia
   scroll, hubiera un mensaje o seis. Con `margin: auto` el hueco existe pero no
   es contenido: con pocos mensajes no hay nada que desplazar, y cuando crecen el
   margen se come solo y empieza el scroll normal.
   ⚠️ NO usar `justify-content: flex-end` para esto: cuando el contenido
   desborda, el principio del chat queda por encima del area desplazable y los
   primeros mensajes se vuelven INALCANZABLES. */
.iv-chat > .iv-msg:first-child { margin-top: auto; }

/* 🚨 AQUI ESTABAN LOS DOS ESPACIADORES del ancla (::before y ::after con
   --iv-pad-top / --iv-pad-bottom, 2026-09-15). Se BORRARON el 2026-09-16:
     - el de arriba es ahora `margin-top: auto` (ver la regla de al lado), que
       hace lo mismo SIN contar como contenido;
     - el de abajo existia para el panel flotante, y el panel se fue.
   De paso se fue el bucle que documentaban: como `padding` hacian crecer la
   caja y ivFitChat() releia ese alto mayor. Nada de esto queda vivo. */

.iv-msg { display: flex; align-items: flex-end; gap: 12px; }
.iv-them { justify-content: flex-start; }
.iv-me { justify-content: flex-end; }

/* ---- la entrada de cada mensaje ----
   🚨 SE ANIMA LA FILA ENTERA (.iv-msg), no la burbuja y el avatar por separado.
   Dos animaciones con la misma duracion van juntas hoy y se desincronizan el
   dia que alguien toque una; una sola no puede. Y asi el avatar entra con su
   burbuja, como en un chat de verdad.
   Cada fila entra DESDE SU LADO: la del entrevistador de izquierda a derecha,
   la del jugador de derecha a izquierda — los signos del translateX son
   opuestos a proposito.
   0,65 s y cubic-bezier(0.16, 1, 0.3, 1): los del After Effects de Omar. El 98%
   del recorrido ocurre en la primera mitad; el resto es la cola lenta.
   ⚠️ Los MISMOS 0,65 s y la MISMA curva estan en app.js (IV_ENTER_MS e
   ivEase), que anima el scroll a la par. Si cambias uno, cambia el otro. */
@keyframes ivInThem {
  from { opacity: 0; transform: translateX(calc(var(--iv-slide) * -1)); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes ivInMe {
  from { opacity: 0; transform: translateX(var(--iv-slide)); }
  to   { opacity: 1; transform: translateX(0); }
}
/* con movimiento reducido: SOLO el fundido, sin desplazamiento */
@keyframes ivFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.iv-them { animation: ivInThem 0.65s cubic-bezier(0.16, 1, 0.3, 1) both; }
.iv-me   { animation: ivInMe 0.65s cubic-bezier(0.16, 1, 0.3, 1) both; }
.iv-avatar { flex: none; display: block; width: 40px; height: 40px; }

/* ---- la burbuja y su colita ----
   🚨 LA COLITA CON BORDE SON DOS TRIANGULOS: ::before del color del borde y,
   encima, ::after del color del relleno, un pelo mas pequeno, que tapa el
   primero por dentro y abre la union con la burbuja.
   ESE TRUCO EXIGE RELLENO OPACO. Con `background: transparent` el triangulo de
   atras se ve entero y la colita sale como un pico macizo del color del borde.
   Por eso la burbuja del entrevistador —que en el mockup PARECE transparente—
   lleva de relleno el fondo de la web, --bg-1: se ve igual y la colita funciona.
   Cada variante solo fija --tail-bd y --tail-bg; la geometria es comun. */
.iv-bubble {
  position: relative;
  border: 2px solid var(--tail-bd);
  background: var(--tail-bg);
  border-radius: 16px;
}
.iv-bubble::before,
.iv-bubble::after {
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  border-style: solid;
}
/* izquierda: apunta al avatar del entrevistador */
.iv-them .iv-bubble::before {
  left: -12px;
  bottom: 10px;
  border-width: 8px 12px 8px 0;
  border-color: transparent var(--tail-bd) transparent transparent;
}
.iv-them .iv-bubble::after {
  left: -8px;
  bottom: 12px;
  border-width: 6px 9px 6px 0;
  border-color: transparent var(--tail-bg) transparent transparent;
}
/* derecha: apunta al avatar del jugador */
.iv-me .iv-bubble::before {
  right: -12px;
  bottom: 10px;
  border-width: 8px 0 8px 12px;
  border-color: transparent transparent transparent var(--tail-bd);
}
.iv-me .iv-bubble::after {
  right: -8px;
  bottom: 12px;
  border-width: 6px 0 6px 9px;
  border-color: transparent transparent transparent var(--tail-bg);
}

/* La del entrevistador es de ANCHO FIJO: siempre lleva lo mismo, boton + barra. */
.iv-audio {
  --tail-bd: var(--iv-bubble-bd);
  --tail-bg: var(--bg-1); /* NO transparent: ver la colita, arriba */
  display: flex;
  align-items: center;
  gap: 12px;
  width: 260px;
  max-width: calc(100% - 52px); /* el avatar y su hueco */
  padding: 8px 14px 8px 8px;
}
.iv-audio-btn {
  flex: none;
  width: 44px; /* objetivo tactil minimo */
  height: 44px;
  padding: 0;
  border: none;
  border-radius: 10px;
  background: none;
  cursor: pointer;
}
.iv-audio-btn img { display: block; width: 100%; height: 100%; }
.iv-audio-btn:disabled { cursor: default; opacity: 0.4; }
.iv-audio-btn:focus-visible { outline: 2px solid var(--iv-ink); outline-offset: 3px; }

.iv-bar {
  flex: 1 1 auto;
  height: 8px;
  border-radius: 4px;
  background: var(--iv-bar-track);
  overflow: hidden;
}
/* La mueve `timeupdate` (~4 veces por segundo); la transicion solo suaviza
   entre dos lecturas. Al acabar se queda al 100%. */
.iv-bar-fill {
  width: 0;
  height: 100%;
  border-radius: 4px;
  background: var(--iv-bar-fill);
  transition: width 0.25s linear;
}

/* Las respuestas SE ADAPTAN AL TEXTO: fit-content, y un tope en porcentaje
   para que una larga parta en lineas sin desbordar. */
.iv-reply {
  width: fit-content;
  max-width: min(78%, calc(100% - 52px));
  padding: 10px 14px;
  color: var(--text);
}
.iv-ok { --tail-bd: var(--iv-ok-bd); --tail-bg: var(--iv-ok-bg); }
.iv-bad { --tail-bd: var(--iv-bad-bd); --tail-bg: var(--iv-bad-bg); }
.iv-line { margin: 0; font-size: 0.95rem; line-height: 1.35; }
.iv-line + .iv-line { margin-top: 6px; }
.iv-line-chosen s {
  color: var(--text-dim);
  text-decoration-thickness: 2px;
}

/* Solo para lector de pantalla: el tachado no suena, esta etiqueta si. No hay
   una clase asi en el proyecto; esta es propia, como todo el bloque. */
.iv-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---- las opciones, DENTRO del chat (2026-09-16) ----
   🚨 Aqui estaba .iv-q-panel, el panel flotante copiado de .rd-q-panel
   (position:absolute abajo, --panel-97, entrada ivPanelUp). Se BORRO entero,
   con su @keyframes y con .iv-hint: las opciones son ahora una fila mas de la
   conversacion, justo debajo de la burbuja del audio.

   La fila se sangra el ancho del avatar mas su hueco (40 + 12) para que las
   opciones queden alineadas con la burbuja, no con el avatar. */
.iv-options-row { padding-left: 52px; }
.iv-options-row .iv-options { width: 100%; }

/* 🚨 `visibility`, NUNCA `display: none`. Las opciones se crean A LA VEZ que la
   burbuja para que la maquetacion sea ya la definitiva y la burbuja no se mueva
   al encenderlas; con `display: none` no ocuparian sitio y volveria el salto.
   Y `visibility: hidden` las saca ademas del tabulador y del lector de
   pantalla, que es justo lo que se quiere mientras habla el entrevistador. */
.iv-options.is-waiting { visibility: hidden; }

/* ---- la pantalla de contexto ----
   Usa .overlay + .intro-card, como el modal de inicio: no se inventa un
   flotante nuevo. Vive dentro de #screen-interview-play. */
.iv-context-text {
  color: var(--text-dim);
  font-size: 1rem;
  line-height: 1.6;
  margin: 0 0 26px;
}
.iv-context-start { min-width: 200px; }

@media (prefers-reduced-motion: reduce) {
  .iv-bar-fill { transition: none; }
  /* los mensajes SIGUEN apareciendo, con fundido y sin movimiento lateral.
     El scroll del ancla lo salta ivAnchor() leyendo reduceMotion(). */
  .iv-them,
  .iv-me { animation-name: ivFadeIn; }
}

/* Las cuatro opciones. Rejilla de UNA columna a proposito: son frases enteras,
   no palabras sueltas como en la radio, y a dos columnas se parten en tres
   lineas cada una. */
.iv-options { display: grid; grid-template-columns: 1fr; gap: 12px; width: 100%; }
.iv-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  border: 2px solid var(--line-soft);
  border-radius: 12px;
  background: var(--tile);
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.iv-option:hover:not(:disabled) { background: var(--tile-hover); transform: translateY(-1px); }
.iv-option:disabled { cursor: default; transform: none; }
.iv-option-badge {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  border-radius: 8px;
  border: 2px solid var(--line-soft);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.78rem; font-weight: 800;
  color: var(--text-dim);
}
.iv-option-text { flex: 1; min-width: 0; }

/* (2026-09-15) Aqui estaban .iv-option.correct y .iv-option.wrong: al responder
   se pintaba la opcion en verde o rojo y las cuatro se quedaban en su sitio. Se
   BORRARON: ahora la respuesta entra al chat como burbuja (.iv-ok / .iv-bad) y
   el panel se vacia. La idea de fondo sigue en pie —al fallar se ENSENA la
   correcta— pero vive dentro de la caja roja. */

/* Las tres vidas */
.iv-lives { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.iv-life {
  width: 20px; height: 20px;
  display: block;
  color: #e0442e;
  transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
}
.iv-life svg { width: 100%; height: 100%; display: block; }
.iv-life.spent { color: var(--line-soft); opacity: 0.55; transform: scale(0.85); }

@media (max-width: 480px) {
  .iv-list-title { font-size: 1.7rem; }
  .iv-list-grid { grid-template-columns: 1fr; gap: 16px; padding: 0 14px 32px; }
  .iv-chat { gap: 14px; padding: 16px 12px 12px; }
  .iv-avatar { width: 32px; height: 32px; }
  .iv-msg { gap: 9px; }
  .iv-audio { width: 230px; max-width: calc(100% - 41px); }
  .iv-reply { max-width: min(84%, calc(100% - 41px)); padding: 9px 12px; }
  .iv-line { font-size: 0.9rem; }
  .iv-options-row { padding-left: 41px; } /* avatar 32 + hueco 9 */
  .iv-options { gap: 8px; }
  .iv-context-text { font-size: 0.94rem; margin-bottom: 20px; }
  .iv-option { padding: 12px 13px; font-size: 0.9rem; }
  .iv-life { width: 17px; height: 17px; }
}

/* ---- las reglas de .hidden de este bloque ---- */
/* 🚨 Sin esto la clase no apaga NADA: este proyecto NO tiene una regla
   generica .hidden, tiene una por componente (ver CLAUDE.md). Poner la clase
   en un elemento nuevo y no anadir su regla es un fallo silencioso: no hay
   error, la clase se pone bien, y el elemento se sigue pintando. */
.iv-list-state.hidden { display: none; }
/* (2026-09-16) Aqui estaban .iv-options.hidden y .iv-q-panel.hidden. Las dos se
   fueron con el panel: las opciones ya no se esconden con una clase, se CREAN
   con la burbuja (invisibles, ver .iv-options.is-waiting) y se BORRAN del DOM
   al contestar. El flotante de contexto no necesita regla propia: es .overlay,
   que si tiene la suya. */
/* 🚨 #iv-end-hint reusa .st-wordlist-hint, la clase de Strands, y
   `.st-wordlist-hint.hidden` NO EXISTE en este archivo — es el mismo fallo
   PREEXISTENTE que ya se rodeo en la radio con `#rd-wordlist-hint.hidden` (ver
   la nota de mas abajo). Se apaga por ID por el mismo motivo y con el mismo
   limite: la regla correcta seria `.st-wordlist-hint.hidden`, que arreglaria de
   paso Strands, Hear It, Emoji Match y Fill in the Blanks, y eso es una
   decision de Omar y no un efecto colateral de esta tarea. */
#iv-end-hint.hidden { display: none; }


/* 🚨 #rd-wordlist-hint reusa .st-wordlist-hint, la clase de Strands, y resulta
   que `.st-wordlist-hint.hidden` NO EXISTE en este archivo: la clase se pone y
   el parrafo se sigue pintando. Es un fallo PREEXISTENTE que alcanza tambien a
   Strands, Hear It, Emoji Match y Fill in the Blanks — medido: con texto
   dentro, 13,6 px que deberian estar apagados y 16 px mas de panel final.

   Aqui se arregla SOLO el elemento de la radio, a proposito. La regla correcta
   seria `.st-wordlist-hint.hidden`, que arreglaria los cuatro juegos de paso, y
   eso es una decision de Omar y no un efecto colateral de esta tarea. Esta en
   el reporte con sus numeros. */
#rd-wordlist-hint.hidden { display: none; }

@media (prefers-reduced-motion: reduce) {
  .rd-q-panel { animation: none; }
  /* La portada de la radio NO se apaga desde aqui: ya no tiene animacion CSS,
     su tamaño lo escribe JS leyendo el fotograma del Lottie. La preferencia se
     mira en rdInitAnims() (app.js), que en ese caso la deja en scale(1) y no
     llega a registrar el `enterFrame`. */
}

@media (max-width: 768px) {
  .rd-list-title { font-size: 1.7rem; }
  .rd-story-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 18px; }
  /* La escena ya no lleva ancho propio aqui: lo manda `height` + `max-width`
     de la regla base. */
  .rd-play-btn { width: clamp(150px, 34vw, 210px); }
  .rd-q-panel { width: 100%; padding: 20px 16px 24px; border-radius: 18px 18px 0 0; }
  .rd-q-prompt { font-size: 1.3rem; margin-bottom: 14px; }
  /* 🚨 UNA COLUMNA. A 2x2, «They were looking for the wrong kind of person»
     parte en cuatro lineas y el panel crece hasta comerse la escena. */
  .rd-options { grid-template-columns: 1fr; gap: 10px; }
  .rd-option { min-height: 58px; padding: 10px 14px 10px 62px; font-size: 0.98rem; }
  .rd-option-badge { width: 40px; height: 32px; left: 10px; font-size: 0.95rem; }
}

@media (max-width: 480px) {
  .rd-list-title { font-size: 1.45rem; letter-spacing: 1px; }
  .rd-list-desc { font-size: 0.88rem; margin-bottom: 20px; }
  .rd-story-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px; padding: 0 14px 32px; }
  .rd-story-name { font-size: 0.94rem; }
  .rd-stage { gap: 16px; padding: 6px 14px 18px; }
  .rd-play-btn { width: clamp(130px, 38vw, 180px); }
  .rd-replays { font-size: 0.78rem; }
  .rd-q-panel { padding: 16px 12px 20px; }
  .rd-q-prompt { font-size: 1.12rem; margin-bottom: 12px; }
  .rd-option { min-height: 52px; padding: 10px 12px 10px 56px; font-size: 0.92rem; }
  .rd-option-badge { width: 36px; height: 30px; left: 8px; font-size: 0.9rem; }
}

/* ================= ISLANDS — LA LISTA DE ISLAS =================
   🚨 ESTAS TARJETAS SE CLONAN DE .game-card (LA PORTADA), NO DE .rd-story-card.
   La lista de la radio perdio su marco el 2026-09-12 y hoy es portada + titulo
   centrado, sin borde ni fondo: copiarla aqui daria justo lo contrario de lo
   pedido. Aqui la tarjeta es CUADRADA, con marco de 4px alrededor de SOLO la
   portada, y el PLAY y el nombre FUERA del marco, en el pie.

   Las reglas estan COPIADAS de .game-card, no compartidas con ella. El dia que
   la portada o las islas cambien por separado —y van a cambiar— compartir
   clases romperia la otra pantalla sin avisar. Mismo motivo que las .rd-*. */
.isl-list-title {
  max-width: 900px;
  margin: 6px auto 8px;
  padding: 0 20px;
  font-size: 2rem;
  font-weight: 900;
  letter-spacing: 2px;
  text-align: center;
}
.isl-list-desc {
  max-width: 640px;
  margin: 0 auto 26px;
  padding: 0 20px;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
  text-align: center;
}

/* Flex centrado con tarjetas de ancho FIJO, igual que .card-grid — no un
   `grid` con auto-fill. Con auto-fill una sola isla saldria estirada a toda la
   columna o pegada al borde izquierdo; asi sale centrada y midiendo lo mismo
   que una tarjeta de la portada, haya una o haya doce. Los tres max-width son
   los mismos de .card-grid para que las dos pantallas rompan a la vez. */
.isl-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 28px;
  padding: 0 16px 60px;
  max-width: 778px;
  margin-inline: auto;
}
@media (max-width: 860px) {
  .isl-grid { max-width: 520px; }
}
@media (max-width: 540px) {
  .isl-grid { max-width: 262px; }
}

/* Sin marco y sin fondo: los tiene .isl-card-art, o sea que el marco rodea
   solo la portada. `overflow: visible` por si algun dia asoma una estrella o
   una medalla por fuera, como en la portada. */
.isl-card {
  position: relative;
  width: 230px;
  border: none;
  border-radius: 14px;
  background: transparent;
  cursor: pointer;
  overflow: visible;
  padding: 0;
  color: var(--text);
  font: inherit;
  transition: transform 0.15s ease;
}
/* Se levanta la tarjeta ENTERA — portada, PLAY y nombre — asi que el transform
   se queda aqui y solo las sombras bajan a .isl-card-art. */
.isl-card:hover,
.isl-card:focus-visible {
  transform: translateY(-4px);
}

/* AQUI ESTA EL MARCO. Cuadrada con `aspect-ratio` y no con un `height` fijo:
   a 230px de alto dejaria de serlo en cuanto la tarjeta se estreche.
   La `transition` NO es decorativa: el halo del hover vive en esta regla, asi
   que sin ella apareceria de golpe en vez de en 150 ms. */
.isl-card-art {
  position: relative;
  height: auto;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: none;
  border-radius: 14px;
  background: var(--panel);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
  transition: box-shadow 0.15s ease;
}
/* 🚨 EL MARCO ES UNA CAPA ENCIMA, NO UN `border`, y el motivo no es estetico:
   un hijo con `inset: 0` se dimensiona contra la caja de RELLENO, asi que con
   un borde de verdad la imagen se calcularia contra 222px en vez de 230 y la
   escala saldria un 3,5% corta. Con la capa, el <img> ocupa los 230 completos.
   `pointer-events: none` NO ES OPCIONAL: la tarjeta entera es un <button> y
   sin eso la capa se come los clics del centro de la portada. */
.isl-card-art::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 4px solid var(--panel-border);
  border-radius: 14px;
  pointer-events: none;
  z-index: 2;
}
/* 🚨 LAS DOS CAPAS DE SOMBRA, y en este orden. `box-shadow` NO se acumula entre
   reglas: lo que declare el hover SUSTITUYE a lo de reposo. Si aqui solo fuera
   el halo morado, al pasar el raton la tarjeta subiria 4px y a la vez PERDERIA
   su sombra, o sea que se leeria como si cayera.
   `:focus-visible` va en los DOS selectores: es lo que ve quien navega con el
   tabulador, y si falta en el segundo el foco deja de verse. */
.isl-card:hover .isl-card-art,
.isl-card:focus-visible .isl-card-art {
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55),
              0 0 22px rgba(155, 48, 255, 0.55);
}

/* `position: absolute` y no el 100% del flex: el porcentaje se resuelve
   distinto entre navegadores cuando la caja es height:auto, y eso hacia que
   unas tarjetas salieran unos px mas altas que otras en la portada.
   El 1.15 es el MISMO de .card-art-img y es deliberado: hace que el marco
   muerda un poco el dibujo. Se repite aqui en vez de compartirse, como el
   resto del bloque. */
.isl-card-img {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  transform: scale(1.15);
  transform-origin: center center;
}

/* Fuera del marco, asi que sin fondo ni esquinas propias. */
.isl-card-footer {
  padding: 12px 0 0;
  text-align: center;
}
.isl-card-play {
  display: block;
  background: var(--accent);
  color: #fff;
  font-weight: 800;
  letter-spacing: 1px;
  border-radius: 8px;
  padding: 8px 0;
  margin-bottom: 8px;
}
.isl-card:hover .isl-card-play { background: var(--accent-bright); }
.isl-card-name { font-weight: 700; font-size: 0.95rem; }

@media (max-width: 768px) {
  .isl-list-title { font-size: 1.7rem; }
}
@media (max-width: 480px) {
  .isl-list-title { font-size: 1.45rem; letter-spacing: 1px; }
  .isl-list-desc { font-size: 0.88rem; margin-bottom: 20px; }
}

/* ================= ISLANDS — LA ESCALERA DE UNA ISLA =================
   Columna VERTICAL con los nodos alternando izquierda y derecha. No la ese
   horizontal del boceto: sus nodos median el 10,5% del ancho, 41 px en un
   movil de 390, por debajo de los 44 px minimos del dedo.

   Sin fondo propio: manda --bg-1 del body (#131B20), no el #1F272C del boceto.
   Y en escritorio NO se estira: una columna estrecha y centrada, como la lista
   de la radio con su max-width. */
.isl-stairs-title {
  max-width: 900px;
  margin: 6px auto 8px;
  padding: 0 20px;
  font-size: 2rem;
  font-weight: 900;
  letter-spacing: 2px;
  text-align: center;
  text-transform: uppercase;
}
.isl-stairs-state {
  max-width: 640px;
  margin: 0 auto 16px;
  padding: 0 20px;
  color: var(--text-dim);
  text-align: center;
}
/* 🚨 Regla .hidden PROPIA: este proyecto no tiene una generica (ver CLAUDE.md). */
.isl-stairs-state.hidden { display: none; }

.isl-path {
  max-width: 360px;
  margin: 14px auto 0;
  padding: 0 16px 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.isl-step {
  display: flex;
  justify-content: center;
  width: 100%;
}
/* El desplazamiento va en `left` y no en `transform`: el transform lo usa el
   hover para levantar el nodo, y dos reglas que escriben transform se pisan. */
.isl-step-left .isl-step-btn { left: -56px; }
.isl-step-right .isl-step-btn { left: 56px; }

.isl-step-btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 4px;
  border: none;
  border-radius: 16px;
  background: transparent;
  color: var(--text);
  font: inherit;
  cursor: pointer;
  transition: transform 0.15s ease;
}
.isl-step-btn:not(:disabled):hover,
.isl-step-btn:focus-visible {
  transform: translateY(-3px);
}
.isl-step-btn:focus-visible {
  outline: 3px solid var(--accent-bright);
  outline-offset: 4px;
}
.isl-step-btn:disabled { cursor: not-allowed; }

/* 110 px: por encima de los 44 del dedo con margen, y cabe con su desplazamiento
   de 56 px hasta en 360 de ancho. */
.isl-step-disc {
  display: block;
  width: 110px;
  height: 110px;
}
/* 🚨 EL JEFE, UN 24% MAS GRANDE: 110 x 1,24 = 136,4 -> 136. Deliberado, del
   boceto: marca el ultimo escalon sin escribir la palabra «jefe». */
.isl-step-boss .isl-step-disc {
  width: 136px;
  height: 136px;
}

.isl-step-stars {
  display: flex;
  gap: 4px;
  font-size: 1.25rem;
  line-height: 1;
}
.isl-star { color: rgba(255, 255, 255, 0.18); } /* hueco vacio */
.isl-star.on { color: #ffc83d; }
.isl-step-name { font-weight: 700; font-size: 0.95rem; }
.isl-step-locked .isl-step-name { color: var(--text-dim); }

/* los conectores de puntos del boceto, del color del borde */
.isl-dots {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 10px 0;
}
.isl-dots i {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--panel-border);
}

@media (max-width: 768px) {
  .isl-stairs-title { font-size: 1.7rem; }
}
@media (max-width: 480px) {
  .isl-stairs-title { font-size: 1.45rem; letter-spacing: 1px; }
}
