/* ============================================================
   CÁSCARA RESPONSIVE — juegos tipo test (título / emoji / opciones)

   Se carga DESPUÉS del style.css del juego: aquí manda todo lo que sea
   colocación, y el style.css de cada juego se queda con la piel (color,
   animaciones, pantallas de premio).

   La pantalla se parte en tres bandas y no hay scroll nunca:

     ┌────────────────┐   CABECERA  · altura propia, pegada arriba
     │   Animales     │
     ├────────────────┤
     │                │   EMOJI     · se come todo el hueco que sobra
     │       🐻       │
     │                │
     ├────────────────┤
     │  [A]     [B]   │   OPCIONES  · altura propia, pegadas abajo
     │  [C]     [D]   │
     └────────────────┘

   Las dos bandas de los extremos se miden solas y por eso caben siempre,
   en el móvil más pequeño y en el más grande. La del medio es la que da
   y toma. El emoji se mide en unidades de CONTENEDOR (cqh/cqw), o sea
   contra el hueco que le ha quedado y no contra el alto de la ventana:
   da igual el teléfono, siempre sale del tamaño que toca.

   FONDO. `--fondo` cae, si nadie lo declara, en art/background.jpg — el
   mismo respaldo que usa el menú cuando no sabe qué categoría enseñar.

   PERO ESE RESPALDO NO SE PINTA, y hay que saberlo antes de fiarse de él. Una
   `url()` relativa escrita en una hoja se resuelve contra LA HOJA, y esta hoja
   se mudó a /game_engine/ el 26 de agosto de 2026: pide
   /game_engine/art/background.jpg, que no existe en ninguna vertical — 404
   comprobado. Hasta la mudanza la ruta valía, porque motor y arte compartían
   carpeta, y el comentario que había aquí lo seguía diciendo («relativa a ÉL
   (/quijotin/)»): esa frase es lo que tapó el agujero, porque describía la
   verdad de antes. Es la misma trampa que tumbó `categorias.css` el mismo día.

   NO SE ARREGLA RENOMBRANDO, y por eso el nombre nuevo no lo arregla: la red
   de debajo la tiene que poner la vertical, que es la única que sabe dónde
   está su arte. Se nota poco porque `juego-comun.js` declara `--fondo` en
   cuanto corre, así que este respaldo sólo haría falta antes de que llegue el
   JavaScript o si `games.json` no carga — que es exactamente cuando más falta
   haría. Aquí hubo
   un `fondo.jpg` propio en la raíz, que era ese mismo campo sin los molinos;
   se borró el 25 de agosto de 2026, cuando `data-categoria` dejó el respaldo
   sin usar y quedó sólo como un duplicado de 184 KB. Hay una segunda variable,
   `--difuso`, que es la misma imagen ya desenfocada y va DETRÁS: las dos
   juntas son lo que hace que el paisaje quede encuadrado igual que en el
   menú (ver «EL PAISAJE, EN DOS CAPAS» más abajo). Quien pone las dos es
   juego-comun.js, y la segunda se la deduce del nombre de la primera.
   Si un juego quiere el suyo
   propio, redeclara la variable en SU style.css y entonces la ruta pasa
   a ser relativa a ese otro fichero:

       :root { --fondo: url('fondo-mar.jpg'); --cielo: #cfe6ff; }

   PARA REPLICARLO EN OTRO JUEGO hacen falta tres líneas en su
   index.html —y ni una en su game.js:

     1. en el <meta viewport>, añadir `viewport-fit=cover` (para que el
        fondo llegue al borde en los móviles con muesca);
     2. detrás de la hoja del juego, y sólo detrás:
            <link rel="stylesheet" href="../juego-layout.css">
     3. antes de su game.js:
            <script src="../juego-fx.js"></script>

   El paisaje se elige en su game.json, que ya es la ficha del juego:

       { "fondo": "bosque.jpg", "cielo": "#cfe6ff" }

   La ruta es relativa a la carpeta del juego, como cabe esperar: de eso
   se encarga juego-comun.js, que la convierte en absoluta antes de
   pasarla. Sin game.json —los hay— se declara en su propia style.css:

       :root { --fondo: url('../mi-juego/bosque.jpg'); }

   OJO con esa segunda forma: dentro de una variable, el navegador
   resuelve las rutas relativas contra la hoja donde se USA la variable
   (ésta, en /quijotin/), no contra la hoja donde se declara. Por eso el
   ../mi-juego/. Es la razón de que la vía recomendada sea game.json.
   ============================================================ */

/* La misma fuente que el banner de pergamino (quijotin/banner). Se pide
   desde aquí y no con un <link> en cada index.html para que adoptar
   esta cáscara siga siendo una sola línea. */
@import url('https://fonts.googleapis.com/css2?family=Bree+Serif&display=swap');

/* Lo que un juego puede querer cambiar NO se declara aquí, sino que se
   lee en su sitio con `var(--x, respaldo)`. Es a propósito: esta hoja
   carga la última —tiene que ganar en colocación— y si declarase estas
   variables en :root machacaría las del juego, que carga antes. Sin
   declaración no hay a quién machacar, y el juego manda.

   Personalizables así:  --fondo  --cielo  --plato  --fuente-titulo
   --tam-elemento  --tam-opcion
   (y --flotar, la velocidad del vaivén del emoji).

   OJO con `--tam-elemento`: no es un ajuste, es un interruptor que apaga
   la tabla de `data-largo`. Leer su bloque antes de usarlo. */
:root {
  /* SÓLO CLARO, Y CON `only`. El WebView de Android TV —TV Bro con su
     interfaz en modo noche— aplica «oscurecimiento algorítmico» a toda página
     que no declare su esquema de color: invierte el texto y los fondos planos y
     deja las imágenes, así que el rótulo del juego salía BLANCO sobre el
     paisaje (visto en un Google TV Streamer el 11 de septiembre de 2026). Esta
     línea le dice que el esquema es claro y que no lo toque. MEDIDO con Chrome
     en auto-dark: `color-scheme: light` a secas NO lo para; `only light` sí, en
     CSS de `:root` o en un <meta name="color-scheme">. Aquí va en la hoja para
     que lo hereden los 25 juegos y los que vengan, sin una línea que recordar;
     los menús, que no cargan esta hoja, llevan el <meta>. */
  color-scheme: only light;
  --canto:  var(--acento-claro, rgba(255,255,255,.92));  /* el canto de los
                     botones se tiñe con el color del juego si lo hay */
  --halo:   0 1px 0 rgba(255,255,255,.9), 0 0 14px rgba(255,255,255,.85);
  --hueco:  clamp(8px, 1.5vh, 16px);
  --margen: max(12px, env(safe-area-inset-left, 0px));
}

/* ── LIENZO ──────────────────────────────────────────────────
   Sin scroll ni rebote: el juego cabe en la ventana por diseño.

   El fondo va a `cover` anclado abajo. Probamos también a encajarlo a
   lo ancho (100%, 78vh, 105vh…) para que se viera el dibujo entero:
   en un móvil de pie sale una tira aplastada que además se queda
   detrás de los botones. Con `cover` se recorta de lado, sí, pero el
   paisaje mantiene su escala y las dunas asoman por encima de la fila
   de botones, que es lo que se ve. --cielo es el color del borde de
   arriba de la imagen y tapa el hueco mientras carga. */
html {
  height: 100%;
  overflow: hidden;
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
}

body {
  height: 100vh;
  height: 100dvh;
  /* Este min-height no sobra: 17 de los juegos traen `min-height:100vh`
     en su hoja, y en iOS 100vh es el viewport GRANDE —el de la barra del
     navegador escondida—, unos 90px más alto que el 100dvh visible. Sin
     anularlo, el min-height gana al height, el body se estira más que la
     pantalla y, como aquí no hay scroll, la fila de abajo se queda
     cortada y fuera de alcance. No se ve en el escritorio ni dentro de
     un iframe, porque ahí 100vh y 100dvh valen lo mismo. */
  min-height: 0;
  margin: 0;
  padding: 0;
  display: block;
  overflow: hidden;
  overscroll-behavior: none;
  /* Sólo el color. El paisaje lo pintan los dos pseudoelementos de aquí
     abajo; esto es lo que se ve mientras los jpg llegan, y --cielo es el
     color del borde de arriba de la imagen para que no se note el hueco. */
  background-color: var(--cielo, #f2db80);
}

/* ── EL PAISAJE DE ARRANQUE, POR CATEGORÍA ───────────────────
   La tabla que decía qué paisaje y qué cielo lleva cada categoría se fue el 27
   de agosto de 2026 a `categorias.css` de cada VERTICAL, que es de donde son
   esos datos. Allí está el porqué entero.

   Dos motivos. El de fondo: los nombres de las categorías, sus dibujos y sus
   colores son de un producto concreto, y el motor sirve a varios. El
   inmediato: aquí estaba ROTA desde la separación del motor —las rutas eran
   relativas a esta hoja, esta hoja se mudó a /game_engine/ y el arte se quedó
   en la vertical, así que `art/fondos/…` apuntaba a /game_engine/art/fondos/,
   que no existe—. Y estaba DUPLICADA, aquí y en la otra hoja del motor, con un
   comentario en cada una recordando actualizar la otra.

   Las páginas de juego la enlazan ellas, junto a esta hoja:
       <link rel="stylesheet" href="../categorias.css">
   Si falta, no se rompe nada: se ve el paisaje de respaldo hasta que
   `juego-comun.js` pone el bueno, que es como estaba antes de que existiera. */

/* ── EL PAISAJE, EN DOS CAPAS ────────────────────────────────
   Son exactamente las mismas dos del menú (el `body::before`/`body::after`
   de /quijotin/index.html), y lo son a propósito.

   Aquí había una sola imagen en el fondo del body, a `background-size: cover`.
   Ésa era la costura que se veía al entrar en un juego: el menú dibuja el
   paisaje ENTERO a lo ancho, anclado abajo, y rellena lo que sobra por arriba
   con una versión desenfocada de él mismo; `cover` en cambio lo agranda hasta
   llenar el alto y se come los lados. El mismo fichero, el mismo dibujo, y el
   molino no caía en el mismo sitio — en una pantalla vertical la diferencia
   era de medio paisaje.
   No molestaba mientras la tarjeta del menú crecía OPACA hasta tapar la
   pantalla: entre un encuadre y el otro había un plano liso de color que no
   dejaba comparar. Desde que la tarjeta se disuelve y deja ver el paisaje que
   hay debajo, el encuadre pasó a ser lo único que seguía delatando que se
   había cambiado de página.

   Por qué DOS capas y no una bien encuadrada: un paisaje 16:9 dibujado entero
   y anclado abajo deja hueco por arriba en cuanto la pantalla es más alta que
   ancha, y ese hueco relleno de color liso se lee como media pantalla de
   pintura. La capa de abajo es la MISMA foto ya desenfocada —viene así cocida
   en un jpg de 2 KB, no hay ningún `filter` en marcha, que costaba un +30% en
   un iPad— y la costura entre las dos se disuelve con una máscara. El porqué
   largo de cada decisión está en el menú, que es donde se tomaron. */
body::before,
body::after {
  content: '';
  position: fixed;
  pointer-events: none;
  background-repeat: no-repeat;
  /* `fixed` los saca del flujo, así que NO son ítems flex por mucho que el
     body de casi todos los juegos sea `display: flex`: sin eso se colarían
     dos cajas vacías en la columna. Y por lo mismo el `overflow: hidden` del
     body tampoco los recorta: su caja es la ventana. */
}
body::before {
  /* Se sale por los cuatro lados: la imagen viene desenfocada de casa y su
     borde lavado no debe caer justo en el canto de la ventana. */
  inset: -9vmax;
  z-index: -2;
  /* Si no hubiera difusa —un juego con `fondo` propio no tiene por qué tener
     horneada la suya—, se pinta la nítida a `cover`, que es exactamente lo que
     hacía este fichero antes: la caída deja el juego como estaba, no roto. Y si
     no hubiera ninguna de las dos, el campo del menú, que sí trae su difuso. */
  background-image: var(--difuso, var(--fondo, url('art/background-blur.jpg')));
  background-position: center top;
  background-size: cover;
}
body::after {
  inset: 0;
  z-index: -1;
  /* jpg y no png por peso: 150-270 KB contra megas (la textura del dibujo
     hincha el png). Vale cualquier formato.
     El respaldo es el campo del menú, y con eso no hay ninguna imagen que viva
     sólo para ser el respaldo de esta hoja. */
  background-image: var(--fondo, url('art/background.jpg'));
  background-position: center bottom;
  background-size: 100% auto;
  /* Las dos cuentas y sus unidades vienen del menú tal cual, y las unidades
     no son intercambiables: `--hueco` en % —que se miden contra el alto de
     esta caja, o sea el de la ventana, y así no hace falta un vh, que en iOS
     miente mientras está la barra del navegador— y el largo del fundido en
     vh, donde un error de 90px da igual. Lo que no se puede es mezclarlas
     dentro del clamp: hay Safari que no lo digiere, y una máscara inválida se
     descarta ENTERA, con lo que vuelve a verse la raya que venía a tapar. */
  --hueco:   calc(100% - 100vw / 1.776);
  --fundido: clamp(0px, calc(100vh - 100vw / 1.776), 7vh);
  -webkit-mask-image: linear-gradient(to bottom,
      rgba(0,0,0,0) var(--hueco),
      rgba(0,0,0,1) calc(var(--hueco) + var(--fundido)));
  mask-image: linear-gradient(to bottom,
      rgba(0,0,0,0) var(--hueco),
      rgba(0,0,0,1) calc(var(--hueco) + var(--fundido)));
}

#app {
  width: 100%;
  max-width: 640px;
  height: 100%;
  max-height: none;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  position: relative;
}

#game-screen {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* ── BANDA ALTA · cabecera ───────────────────────────────────
   Sin caja ni velo: el título va directo sobre el paisaje, que es lo
   que hace que la cabecera parezca parte del juego y no una barra
   pegada encima. Lo que garantiza que se lea sobre cualquier fondo es
   el halo (--halo): una sombra blanca difusa alrededor de la letra.

   El botón de casa flota: va fijo en la esquina y NADIE le reserva
   sitio. El título se centra en la pantalla entera y las barras van de
   borde a borde. Lo que las mantiene fuera de su camino es que empiezan
   por debajo de él (el botón ocupa hasta los 56px de alto, y el título
   ya deja las barras más abajo). La única excepción es el móvil
   tumbado, donde no hay alto para eso: ver el bloque del final. */
header {
  flex: 0 0 auto;
  text-align: center;
  padding: calc(env(safe-area-inset-top, 0px) + 10px) 14px 4px;
  /* Resplandor blanco sobre paisaje claro y sombra sobre paisaje oscuro:
     lo elige juego-comun.js según el `cielo` de la categoría, que es el
     color que hay justo detrás del título. Sin ficha se queda con el
     halo de siempre. */
  text-shadow: var(--halo-titulo, var(--halo));
}

h1 {
  /* El color NO es el de la píldora del juego a secas: es ese mismo tono
     dado la vuelta cuando el paisaje empieza en sombra (caligrafía,
     verbos), porque ahí un título oscuro no se lee. Lo calcula
     juego-comun.js; el respaldo es lo que hacía cada juego en su hoja. */
  color: var(--titulo, var(--acento, inherit));
  font-family: var(--fuente-titulo, 'Bree Serif', Georgia, serif);
  font-weight: 400;                 /* Bree Serif sólo trae un grosor; con
                                       900 el navegador lo engorda a mano
                                       y sale sucio */
  letter-spacing: normal;
  font-size: clamp(1.6rem, 7.2vw, 2.6rem);
  line-height: 1.15;
  /* Si el nombre no cabe en una línea, que las dos queden parecidas en
     vez de dejar una huérfana: "Minúsculas (あいうえお)" partía como
     "…(あい / うえお)". Donde no esté soportado simplemente parte como
     siempre, así que no hay nada que remendar. */
  text-wrap: balance;
  /* este margen no es sólo aire: es lo que empuja las barras por debajo
     del botón de volver sin tener que apartarlas de lado */
  margin-bottom: 18px;
  /* Y este hueco NO descentra: es igual a los dos lados, así que el
     título sigue en el medio de la pantalla. Lo único que hace es que
     un nombre largo parta en dos líneas en vez de meterse por debajo
     del botón. "Animales" ni se entera; "Mayúsculas (あいうえお)" sí. */
  padding: 0 44px;
}

/* El emoji del juego ya está en la tarjeta del selector y en la pestaña;
   en el título sobraba. Se oculta en vez de borrarlo del HTML porque lo
   escribe juego-comun.js desde game.json. */
#title-emoji { display: none; }

#level-info { gap: 4px; }

/* La cabecera se queda en el título y las dos barras. El rótulo de
   fase, el "Nivel 1" y el porcentaje decían con palabras lo que ya
   dibujan las barras, y en una pantalla de juego para niños cada línea
   de texto de más es ruido. Los tres siguen en el HTML —ocultos, no
   borrados— porque game.js y juego-comun.js escriben en ellos. */
#phase-label, #level-text, #accuracy-pct { display: none; }

/* Las barras van de un borde a otro: sin el porcentaje al lado, el
   único tope por la derecha es el aire del <header>. Y sin la caja
   detrás necesitan su propio contraste: canal blanco translúcido y un
   filo oscuro finísimo para que no se desdibujen sobre las partes
   claras del paisaje. */
#level-row, #accuracy-row { max-width: none; }

#level-bar-bg, #accuracy-bar-bg {
  /* El canal vacío es casi un velo: deja pasar el paisaje y no compite
     con él. Lo que tiene que verse es la parte llena, que va opaca. El
     filo interior es lo único que le da forma cuando está a cero. */
  background: rgba(255,255,255,.28);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.10);
}

#level-bar-bg    { height: 15px; }
#accuracy-bar-bg { height: 10px; }

/* La barra de abajo —lo que falta para el peldaño siguiente— iba en `#ccc` en
   los CATORCE juegos que la tienen, con la misma regla copiada catorce veces.
   Un gris de sistema es lo único sin temperatura de una pantalla que por lo
   demás es un cuadro, y encima esa barra dice algo que importa: vacía no
   significa «acabo de empezar» sino «un fallo más y bajo de peldaño».

   Sube aquí en vez de arreglarse catorce veces, que es lo que este fichero
   viene haciendo con todo lo que estaba copiado. Los `#ccc` de las hojas de
   juego siguen ahí y ya no se ven —esta hoja carga detrás y gana—; se dejan
   porque son un respaldo de verdad: si `categorias.css` no cargara, esto cae
   al `#ccc` y la barra sigue teniendo forma.

   Va con `--acento-medio` y no con `--acento`: la de arriba ya usa el acento
   pleno, y lo que separa a las dos es que ésta es más clara y más fina. */
#accuracy-bar { background: var(--acento-medio, #ccc); }

/* ── BANDA MEDIA · el hueco que sobra ────────────────────────
   `main` es una columna flexible; la tarjeta del elemento se lleva todo
   el sobrante (flex:1) y el resto —mensaje y opciones— ocupa lo suyo y
   se queda abajo. El `order` recoloca el mensaje entre medias sin tocar
   el HTML, para poder replicar la hoja sobre juegos ya escritos. */
main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--hueco);
  padding: var(--hueco) var(--margen)
           calc(env(safe-area-inset-bottom, 0px) + var(--hueco));
}

#element-card {
  order: 0;
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  position: relative;
  /* el paisaje es el decorado: aquí no hay tarjeta que lo tape */
  background: none;
  box-shadow: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  /* la caja se declara contenedor de tamaño: a partir de aquí 1cqh es
     el 1% de ESTE hueco, y el emoji puede medirse contra él */
  container-type: size;
  /* Y no se queda plantado: sube y baja despacio. Es lo que separa una
     pantalla viva de una lámina. Va aquí y no sobre #element-text para
     no chocar con el `pop` de cada pregunta nueva, que también mueve
     el transform. El recorrido es un % de la propia caja, así que se
     ajusta solo al tamaño de la pantalla. */
  animation: flotar var(--flotar, 3.8s) ease-in-out infinite;
}

@keyframes flotar {
  0%, 100% { transform: translateY(0);      }
  50%      { transform: translateY(-2.2%);  }
}

/* La entrada de cada pregunta nueva: SÓLO funde, sin cambiar de tamaño.

   Los juegos la traían como `pop 0.35s ease-out`, de scale(.4) a 1.12 y
   de vuelta a 1. Eso no se ve como un gesto, se ve como un parpadeo.
   Se probó a suavizarlo (menos recorrido y más tiempo) y seguía
   chirriando, por una razón de fondo: el emoji YA se está moviendo todo
   el rato con `flotar`. Un cambio de tamaño encima de un movimiento
   continuo no se lee como "ha entrado uno nuevo", se lee como que algo
   ha fallado. Un fundido no compite con el vaivén y se entiende solo.

   (De paso: el intento intermedio dejó una lección. Alargar la duración
   no sirve de nada si la curva amontona el movimiento al principio —con
   cubic-bezier(.22,.85,.3,1) el 63% del recorrido se gastaba en la
   quinta parte del tiempo, así que 600ms "lentos" se veían más bruscos
   que los 350ms originales.)

   Si algún día se quiere volver a un gesto de tamaño, que sea evidente
   —de 0.6 para arriba— y no un micro-zoom: lo que no se nota a
   propósito parece un fallo.

   Lleva nombre propio (`aparecer`) para no depender del @keyframes que
   traiga cada juego, y `backwards` para que el primer fotograma sea ya
   el de partida. */
#element-text.pop {
  animation: aparecer .5s ease-out backwards;
}

@keyframes aparecer {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Quien haya pedido menos movimiento en su sistema, que no flote ni
   vea entrar nada dando saltos. */
@media (prefers-reduced-motion: reduce) {
  #element-card       { animation: none; }
  #element-text.pop   { animation: none; }
}

/* Peana: un resplandor muy flojo detrás del emoji. Lo despega del fondo
   sin dibujar una caja, y sirve igual si el fondo es claro u oscuro. */
#element-card::before {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 92%; height: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side,
              rgba(255,255,255,.42), rgba(255,255,255,0) 72%);
  pointer-events: none;
}

/* El tamaño de serie lo manda el ANCHO, y está puesto para el peor caso:
   hay juegos que meten aquí una frase entera (verbos: "Hoy yo ...") y ya
   llenan el 100%. Eso deja pequeño lo corto: una sílaba de dos letras se
   queda en el 29% del hueco.

   PERO ESO YA NO HAY QUE ARREGLARLO A MANO: para eso está la tabla de
   `data-largo` de más abajo, que da un tamaño por cada longitud y hoy es
   la que resuelve el caso. `--tam-elemento` es la salida de emergencia,
   no la perilla de serie, y tiene un precio que hay que saber antes:

   ⚠ FIJAR `--tam-elemento` NO AJUSTA EL TAMAÑO: APAGA EL AJUSTE. La tabla
   escribe `--auto-tam`, que es sólo el RESPALDO de `--tam-elemento`, así
   que un valor fijo en la hoja del juego se lleva por delante la longitud
   entera. Es lo que tuvieron `minusculas/` y `silabas/` hasta el 29 de
   agosto de 2026, cada uno con un comentario que declaraba un supuesto
   («aquí lo que se enseña es UNA letra») falso desde el primer día: los
   dos tienen fase de palabras, y toda palabra de cuatro letras o más
   salía partida en dos o tres líneas, con la última cayendo por detrás de
   los botones. Lo avisó un padre. En horizontal no se veía.

   Si un juego necesita de verdad un tamaño propio, hay dos salidas que no
   apagan nada:

     · una clase `len-*` sobre #element-text desde su game.js (escriben
       `font-size` directo, ganan a la variable, y se ponen por ronda con
       la longitud de esa ronda) — es lo que hace `minusculas-mayusculas`;
     · `--tam-elemento` calculado por JS con el largo del texto, como los
       tres de verbos. Perilla, no constante.

   Y si aun así se escribe a mano: cqh/cqw son % del hueco del emoji, no
   de la ventana. Con letra Nunito 900 cada carácter ocupa ~0.72 em (0.815
   en mayúsculas), así que para que entren N caracteres el término de
   ancho no debe pasar de 100/(N*0.72) por ciento, y conviene dejar un 15%
   de aire — contando el TEXTO MÁS LARGO del `levels.jsonl`, que no es el
   que se tiene en la cabeza al escribir la hoja. */
#element-text {
  position: relative;
  max-width: 100%;
  text-align: center;
  overflow-wrap: anywhere;
  line-height: 1.1;
  text-shadow: var(--halo);

  /* Respaldo para quien no tenga unidades de contenedor. */
  font-size: min(30vh, 24vw);
}

/* OJO CON EL RESPALDO: el truco de las dos declaraciones seguidas
   —la buena detrás de la de repuesto, y que gane la última que el
   navegador entienda— NO FUNCIONA cuando la buena lleva `var()`.

   Así estaba antes, y estaba roto:

       font-size: min(30vh, 24vw);                    <- repuesto
       font-size: var(--tam-elemento, var(--auto-tam, min(30cqh, 24cqw)));

   Un `var()` SIEMPRE es válido al leer la hoja, así que la segunda
   línea tira la primera a la basura sin mirar qué hay dentro. El
   contenido no se mira hasta más tarde, al calcular el valor, y para
   entonces ya no queda repuesto: en un navegador que no conoce `cqh`
   el valor sale inválido y font-size —que se hereda— se queda con lo
   que trae el padre. Los 16px del body.

   Eso es lo que se veía en un iPad con Safari 15: la palabra a
   adivinar en cuerpo de texto corrido mientras el resto de la
   pantalla —que va en rem y en px— salía perfecta. Y no era cosa de
   un juego: le pasaba a TODOS los que enseñan una palabra. Los de
   emoji se libraban de milagro, porque la regla de `.emoji` sí usa el
   par de declaraciones sin `var()` y ahí el truco sí vale.

   Con @supports la pregunta se hace antes y de frente, que es donde
   tiene que hacerse. */
@supports (font-size: 1cqh) {
  #element-text {
    font-size: var(--tam-elemento, var(--auto-tam, min(30cqh, 24cqw)));
  }
}

#element-text.len-md { font-size: min(25vh, 19vw); font-size: min(25cqh, 19cqw); }
#element-text.len-lg { font-size: min(21vh, 15vw); font-size: min(21cqh, 15cqw); }
#element-text.len-xl { font-size: min(18vh, 12vw); font-size: min(18cqh, 12cqw); }

/* ── TAMAÑO SEGÚN EL LARGO ────────────────────────────────
   `data-largo` lo escribe juego-comun.js contando los caracteres de lo
   que hay que adivinar. Con eso, una letra sale enorme y una palabra de
   siete cabe, en el mismo juego y sin tocar su game.js: `letras` enseña
   "A" y también "BOTELLA".

   Los anchos no son a ojo: en Nunito 900 cada mayúscula ocupa 0.815 em,
   así que para N caracteres el término de ancho es 85/(N*0.815) por
   ciento —el 85 es dejar aire—. El de alto baja despacio para que en
   una pantalla apaisada no se coma la banda.

   Ponen `--auto-tam`, no `font-size`, para que un juego que haya fijado
   `--tam-elemento` en su hoja siga mandando: la regla de arriba lee
   primero el suyo y sólo si no hay usa éste. **Que es justo lo que
   convierte un `--tam-elemento` fijo en un interruptor de apagado de esta
   tabla**, con el precio que cuenta el comentario de arriba. */
#element-text[data-largo="1"] { --auto-tam: min(52cqh, 55cqw); }
#element-text[data-largo="2"] { --auto-tam: min(48cqh, 52cqw); }
#element-text[data-largo="3"] { --auto-tam: min(44cqh, 34cqw); }
#element-text[data-largo="4"] { --auto-tam: min(40cqh, 26cqw); }
#element-text[data-largo="5"] { --auto-tam: min(36cqh, 20cqw); }
#element-text[data-largo="6"] { --auto-tam: min(34cqh, 17cqw); }
#element-text[data-largo="7"] { --auto-tam: min(32cqh, 14cqw); }
#element-text[data-largo="8"] { --auto-tam: min(30cqh, 12cqw); }
#element-text[data-largo="9"] { --auto-tam: min(28cqh, 11cqw); }

/* La misma tabla para quien no tenga unidades de contenedor. No puede
   ir en `--auto-tam` como la de arriba: ahí el valor no se mira hasta
   que se sustituye, y es justo entonces cuando falla. Aquí se escribe
   el font-size directamente y @supports decide antes de leerlo.

   Las cifras son las de arriba convertidas a ventana: el hueco mide
   ~55% del alto y ~70% del ancho de la pantalla en las tallas donde
   esto importa (iPad de 768 a 1024). Redondeadas a la baja a
   propósito: un respaldo que se queda corto se lee, uno que se pasa
   se sale de la caja.

   El factor de alto es el 55 y no el 65 que salen de medir, porque el
   hueco tiene tope de ancho (la cáscara no deja pasar de ~616px) y la
   ventana no. En una pantalla ancha y baja el término de alto ganaba
   y una palabra de tres letras se salía por los lados —se partía en
   dos líneas—. Bajándolo, el que manda en el iPad sigue siendo el de
   ancho, que no se mueve, y el caso raro deja de romperse.

   Va ANTES de la regla de `.emoji` y no después: las dos pesan lo
   mismo en el cálculo de especificidad (un id y un trozo más), así
   que decide el orden, y el emoji tiene que seguir mandando sobre el
   largo. */
@supports not (font-size: 1cqh) {
  #element-text[data-largo="1"] { font-size: min(29vh, 38vw); }
  #element-text[data-largo="2"] { font-size: min(26vh, 36vw); }
  #element-text[data-largo="3"] { font-size: min(24vh, 24vw); }
  #element-text[data-largo="4"] { font-size: min(22vh, 18vw); }
  #element-text[data-largo="5"] { font-size: min(20vh, 14vw); }
  #element-text[data-largo="6"] { font-size: min(19vh, 12vw); }
  #element-text[data-largo="7"] { font-size: min(18vh, 10vw); }
  #element-text[data-largo="8"] { font-size: min(17vh,  8vw); }
  #element-text[data-largo="9"] { font-size: min(15vh,  7vw); }
}

/* Cuando lo que hay que adivinar es un emoji manda el alto del hueco.
   Va DESPUÉS de las reglas de data-largo a propósito: tienen la misma
   especificidad, así que decide el orden y aquí queremos que gane ésta.
   no lleva halo de texto (saldría un borrón de color) sino sombra de
   figura, que recorta la silueta. */
#element-text.emoji {
  font-size: min(58vh, 56vw);
  font-size: min(74cqh, 72cqw);
  line-height: 1;
  text-shadow: none;
  filter: drop-shadow(0 10px 14px rgba(80,55,10,.28));
}

/* ── Mensaje de acierto / fallo ──────────────────────────────
   No se ve: el resultado se cuenta con luz y movimiento (juego-fx.js),
   no con una palabra que hay que leer. El div sigue ahí porque es la
   señal que escucha juego-fx.js —el juego le pone `fb-correct` o
   `fb-wrong`— y porque devolver su hueco al emoji es gratis. */
#feedback { display: none; }

/* ── BANDA BAJA · las cuatro opciones ────────────────────────
   Pegadas abajo y enteras SIEMPRE: el alto sale de un clamp contra la
   ventana, con tope mínimo de dedo (58px) y máximo de tableta. */
#options-grid {
  order: 2;
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--hueco);
  margin: 0;
}

.option-btn {
  /* Mismo caso que --tam-elemento: el tamaño de serie está puesto para
     que quepa una palabra larga ("murcielago"), así que en un juego de
     letras sueltas la letra sale diminuta dentro de un botón grande. El
     juego que sepa que sus opciones son cortas que lo diga en SU hoja:
         :root { --tam-opcion: clamp(2rem, 9.5vw, 3.6rem); }
     Las clases len-* siguen mandando por encima, así que una palabra
     larga se encoge igual y no se sale. */
  --fs: var(--tam-opcion, clamp(1.5rem, 7.5vw, 2.8rem));
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(58px, 10.5vh, 96px);
  min-height: 0;
  padding: 6px 10px;
  font-size: var(--fs);
  line-height: 1.15;
  overflow-wrap: anywhere;
  background: var(--plato, rgba(255,255,255,.84));
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border: 3px solid var(--canto);
  border-radius: 18px;
  box-shadow: 0 6px 16px rgba(60,40,10,.14);
}

.option-btn.len-md  { --fs: clamp(1.3rem,  6.2vw, 2.3rem); }
.option-btn.len-lg  { --fs: clamp(1.15rem, 5.4vw, 2rem);   }
.option-btn.len-xl  { --fs: clamp(1rem,    4.6vw, 1.7rem); }
.option-btn.len-xxl { --fs: clamp(.9rem,   4vw,   1.45rem); }

@media (hover: hover) {
  .option-btn:hover:not(.disabled) {
    background: rgba(255,255,255,.95);
    transform: translateY(-2px);
    box-shadow: 0 10px 22px rgba(60,40,10,.2);
  }
}

/* ── Móvil tumbado ───────────────────────────────────────────
   Tumbado, el hueco del medio se estrecha hasta desaparecer, así que
   las cuatro opciones pasan a una sola fila y el emoji recupera alto. */
@media (orientation: landscape) and (max-height: 560px) {
  #app     { max-width: 900px; }
  header   { padding-top: calc(env(safe-area-inset-top, 0px) + 4px);
             padding-bottom: 2px; }
  h1       { font-size: clamp(1.15rem, 3.6vh, 1.6rem); margin-bottom: 3px; }
  /* Tumbado no hay alto para meter las barras por debajo del botón de
     casa, así que aquí sí se apartan de lado. Cuesta 52px de 816: no se
     nota. */
  #level-info { padding-left: 52px; }
  #options-grid { grid-template-columns: repeat(4, 1fr); }
  .option-btn   { height: clamp(48px, 18vh, 76px); }

  /* Y con ellas la letra. Hasta ahora sólo se bajaba el ALTO del botón, y eso
     basta para una palabra latina pero no para el kana, que ocupa el cuadratín
     entero: en `letras`, nivel de palabras, えれふぁんて partía en dos líneas
     y se salía por abajo (texto de 86px en una caja de 75px).

     Los números salen de la cuenta del botón: con 4 columnas mide un cuarto
     del ancho menos los huecos, o sea ~21vw, y quitando el relleno quedan
     ~19vw útiles. A `n` kana por botón toca 19/n vw. El tope en px es para
     que en una ventana ancha de escritorio no siga creciendo: la fila ya no
     lo hace, porque #app está topado a 900px.

     Cada clase se dimensiona para el largo que significa —pelada hasta 4,
     len-md 5, len-lg 6, len-xl 7, len-xxl 8 o más—, contando kana. Con
     letras latinas, que ocupan ~0.55em, sobra sitio de largo. */
  .option-btn         { --fs: min(4.1vw, 37px); }
  .option-btn.len-md  { --fs: min(3.4vw, 30px); }
  .option-btn.len-lg  { --fs: min(2.9vw, 26px); }
  .option-btn.len-xl  { --fs: min(2.5vw, 22px); }
  .option-btn.len-xxl { --fs: min(2.2vw, 19px); }
  #feedback     { min-height: 20px; font-size: 1rem; }
  #element-text.emoji { font-size: min(70cqh, 38cqw); }
}

/* ── Pantallas de premio ─────────────────────────────────────
   Encima del paisaje: cristal esmerilado en vez de un color plano, para
   que no dependa del tinte de cada juego. */
.overlay {
  background: rgba(255,255,255,.78);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
  backdrop-filter: blur(16px) saturate(1.1);
  padding: max(16px, env(safe-area-inset-top, 0px))
           max(16px, env(safe-area-inset-left, 0px))
           max(16px, env(safe-area-inset-bottom, 0px));
}
