/* ==========================================================================
   Votación del Contraeclipse

   Mismo sistema visual que la app (fondo blanco, azul de acción, esquinas
   muy redondeadas, Montserrat) pero hoja aparte y autocontenida. El motivo
   está en votar.html: esta página se abre desde un enlace suelto y no
   tiene por qué arrastrar los 123 KB de style.css.

   Regla estructural, la misma que en la app: la pantalla NUNCA scrollea de
   cuerpo entero. Cada pantalla es una columna fija (cabecera / cuerpo /
   acciones) y solo el bloque de contenido puede desbordar.
   ========================================================================== */

:root {
  --bg: #ffffff;
  --surface: #f4f6fb;
  --surface-2: #e9edf7;
  --border: #e2e7f2;

  --ink: #10143a;
  --ink-dim: #5a6076;
  --ink-faint: #8d94a8;

  --blue: #2b59ff;
  --blue-dark: #1e3fc4;
  --blue-soft: #e8edff;

  --success: #12a45f;
  --danger: #e0455a;
  /* El rojo del corazón del voto. No es el --danger de los errores a
     propósito: son dos mensajes opuestos y compartir color los mezcla. */
  --corazon: #ff2d55;

  --radius-pill: 999px;
  --radius-lg: 24px;
  --radius-md: 16px;
  --radius-sm: 12px;

  --pad: 1.25rem;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  font-family: "Montserrat", system-ui, sans-serif;
}

* { box-sizing: border-box; }

/* El atributo `hidden` tiene que esconder, siempre.
   ----------------------------------------------------------------------
   El navegador lo implementa con `[hidden] { display: none }` en su hoja
   por defecto, que pierde contra CUALQUIER `display` escrito aquí. Como
   .btn declara `display: inline-flex`, poner hidden a un botón no hacía
   absolutamente nada: en las pruebas, después de la tercera tanda seguían
   a la vista el botón de "votar otra tanda" y el de "reintentar".

   Es la misma trampa que ya está documentada en style.css, donde había
   caído tres veces. Aquí la regla general va desde el principio. El
   !important se lo ha ganado: "está oculto" no admite excepciones. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

/* En iOS, 100vh cuenta con la barra del navegador escondida, así que el
   bloque de botones queda debajo del borde justo al entrar. 100dvh mide
   lo que de verdad se ve. Se deja 100vh delante como respaldo. */
.pantalla-marco {
  position: relative;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

.pantalla {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  padding:
    calc(var(--safe-top) + 0.75rem)
    calc(var(--safe-right) + var(--pad))
    calc(var(--safe-bottom) + 0.75rem)
    calc(var(--safe-left) + var(--pad));
  max-width: 820px;
  margin: 0 auto;
}

.pantalla--activa { display: flex; }

/* La pantalla del duelo respira menos por los lados que las demás.
   Son unos 40 px de ancho extra, que no se notan en una foto vertical
   (limitada por la altura) y son un 12 % más de imagen en una apaisada,
   que está limitada justo por el ancho. Cuando lo único que hay que hacer
   en una pantalla es mirar una foto, el margen bonito sobra. */
.pantalla--duelo {
  padding-left: calc(var(--safe-left) + 0.6rem);
  padding-right: calc(var(--safe-right) + 0.6rem);
}

/* --------------------------------------------------------------------------
   Estructura
   -------------------------------------------------------------------------- */

.cabecera { flex: 0 0 auto; padding-bottom: 0.5rem; }

.cabecera--duelo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  justify-content: space-between;
  padding-bottom: 0.6rem;
}

.kicker {
  margin: 0 0 0.2rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue);
}

h1 {
  margin: 0;
  font-size: clamp(1.5rem, 6vw, 2.1rem);
  font-weight: 800;
  line-height: 1.14;
  letter-spacing: -0.015em;
}

.cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.cuerpo--centro {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1rem;
}

.acciones {
  flex: 0 0 auto;
  padding-top: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.acciones--duelo { padding-top: 0.6rem; }

/* Texto general, un punto más grande que antes.
   Se leyó en un móvil al sol y 0,95 rem para las instrucciones era justo
   lo que hace que la gente se las salte. */
.lede {
  margin: 0.4rem 0 1.1rem;
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--ink);
}

.lede--centro { text-align: center; }

.nota {
  margin: 0.6rem 0 0;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink-dim);
  text-align: center;
}

.nota--copiado { color: var(--success); font-weight: 700; }
.nota--premio { margin-top: 1rem; }

/* --------------------------------------------------------------------------
   La portada de la votación
   --------------------------------------------------------------------------

   La foto de cabecera va a sangre, tocando los tres bordes de arriba.
   Vive DENTRO de .cuerpo, que es la parte que scrollea, para que se vaya
   al bajar en vez de comerse un tercio de la pantalla para siempre.

   La forma de conseguir la sangre es quitarle el acolchado a la PANTALLA
   y devolvérselo al texto, no poner márgenes negativos en la foto. Se
   probó con negativos y no funciona: .cuerpo tiene `overflow-y: auto`, y
   en una caja con scroll lo que se sale por ARRIBA o por la IZQUIERDA no
   desborda, se recorta y no hay forma de llegar a ello. O sea que el
   margen negativo no subía la foto, le cortaba una tira. */

.pantalla--portada {
  padding-top: 0;
  padding-left: 0;
  padding-right: 0;
}

.portada {
  position: relative;
  margin: 0 0 1.1rem;
  overflow: hidden;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: #0d0f1c;
}

.portada img {
  display: block;
  width: 100%;
  height: auto;
  /* La proporción del original. Escrita a mano además de en el HTML para
     que el hueco esté reservado aunque la imagen no llegue nunca. */
  aspect-ratio: 1200 / 765;
  object-fit: cover;
}

/* El velo de abajo.
   La mitad inferior de la foto ya es casi negra (luminancia media medida:
   20-24 sobre 255 en el último tercio), así que el blanco se lee de
   sobra. Esto está para el caso malo: en ese tercio hay alguna
   fotografía suelta más clara que el resto, y el titular no puede
   depender de qué mosaico le toque debajo. */
.portada::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 55%;
  background: linear-gradient(
    180deg,
    rgba(8, 10, 22, 0) 0%,
    rgba(8, 10, 22, 0.45) 55%,
    rgba(8, 10, 22, 0.8) 100%
  );
  pointer-events: none;
}

.portada-titulo {
  position: absolute;
  z-index: 2;
  left: var(--pad);
  right: var(--pad);
  bottom: 1rem;
  margin: 0;
  color: #fff;
  font-size: clamp(1.35rem, 5.6vw, 2.1rem);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.015em;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.6);
  text-wrap: balance;
}

/* El texto recupera el acolchado que se le ha quitado a la pantalla. */
.portada-cuerpo {
  padding-left: calc(var(--safe-left) + var(--pad));
  padding-right: calc(var(--safe-right) + var(--pad));
}

.bloque-titulo {
  margin: 1.4rem 0 0.8rem;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--ink);
}

/* La nota de protección de datos. Tiene que estar y tiene que leerse,
   pero no compite con las instrucciones: va la última y en pequeño. */
.nota--legal {
  margin-top: 1.2rem;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/* El bloque del botón, separado del texto con una línea y un poco de
   sombra. No cambia el comportamiento (ya estaba clavado abajo por ser
   `flex: 0 0 auto` dentro de una pantalla de altura fija): lo que hace es
   que SE VEA que está clavado, en vez de parecer el final del texto. */
.acciones--fija {
  border-top: 1px solid var(--border);
  padding: 0.75rem calc(var(--safe-right) + var(--pad)) 0 calc(var(--safe-left) + var(--pad));
  background: var(--bg);
  box-shadow: 0 -10px 18px -12px rgba(16, 20, 58, 0.25);
}

/* --------------------------------------------------------------------------
   Intro: pasos y avisos
   -------------------------------------------------------------------------- */

.pasos {
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.pasos li {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
  font-size: 1.02rem;
  line-height: 1.5;
  color: var(--ink-dim);
}

.pasos strong { color: var(--ink); font-weight: 700; }

.paso-num {
  flex: 0 0 auto;
  width: 1.85rem;
  height: 1.85rem;
  border-radius: var(--radius-pill);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-size: 0.9rem;
  font-weight: 800;
  display: grid;
  place-items: center;
}

.aviso {
  background: var(--surface);
  border-radius: var(--radius-md);
  padding: 1rem 1.1rem;
  margin-bottom: 0.9rem;
}

.aviso--premio { background: var(--blue-soft); }

.aviso-titulo {
  margin: 0 0 0.5rem;
  font-size: 1rem;
  font-weight: 800;
  color: var(--ink);
}

.aviso-texto {
  margin: 0 0 0.6rem;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink-dim);
}

.aviso-texto:last-child { margin-bottom: 0; }
.aviso-texto strong { color: var(--ink); font-weight: 700; }

/* --------------------------------------------------------------------------
   Botones
   -------------------------------------------------------------------------- */

.btn {
  appearance: none;
  border: 0;
  border-radius: var(--radius-pill);
  padding: 1rem 1.4rem;
  font-family: inherit;
  font-size: 1.05rem;
  font-weight: 700;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  text-decoration: none;
  transition: background 0.15s ease, transform 0.08s ease, opacity 0.15s ease;
  /* 52 px de alto: el mismo mínimo táctil que usa la app, pensado para
     pulsar con una mano sujetando el móvil en alto. */
  min-height: 52px;
}

.btn:active { transform: scale(0.985); }
.btn--block { width: 100%; }
.btn--primary { background: var(--blue); color: #fff; }
.btn--primary:hover { background: var(--blue-dark); }
.btn--ghost { background: var(--surface); color: var(--ink); }
.btn--ghost:hover { background: var(--surface-2); }

/* Apagado, pero vivo.
   ----------------------------------------------------------------------
   El botón de elegir está apagado hasta que se han visto las dos fotos, y
   se marca con esta clase MÁS aria-disabled, nunca con el atributo
   `disabled`. Un <button disabled> no recibe clics de ninguna clase, y
   aquí hace falta que sí: al pulsarlo antes de tiempo, votar.js lleva a
   la foto que falta y explica por qué. Un botón que no reacciona parece
   una página rota, y esto se descubrió probándolo, no leyéndolo.

   El atributo `disabled` de verdad sigue existiendo para el resto de
   botones, donde sí significa "no hay nada que hacer aquí". */
/* El botón mientras espera al servidor. Se apaga un poco y deja de
   responder, pero sigue siendo un botón con su texto: esperar en un botón
   que dice lo que está haciendo se lleva mucho mejor que esperar en una
   pantalla de carga. */
.btn--esperando { opacity: 0.65; pointer-events: none; }

.btn--apagado {
  background: var(--surface-2);
  color: var(--ink-faint);
}

.btn[disabled] {
  background: var(--surface-2);
  color: var(--ink-faint);
  cursor: default;
}

.btn-ico {
  width: 1.15rem; height: 1.15rem;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

.btn-icono {
  appearance: none;
  border: 0;
  background: var(--surface);
  color: var(--ink-dim);
  width: 40px; height: 40px;
  border-radius: var(--radius-pill);
  display: grid; place-items: center;
  cursor: pointer;
  flex: 0 0 auto;
}
.btn-icono svg {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round;
}

/* --------------------------------------------------------------------------
   Cabecera del duelo
   -------------------------------------------------------------------------- */

.contador { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; align-items: flex-start; }

/* "Duelo 3 de 7": lo más importante de la cabecera.
   Blanco sobre azul y grande, porque es lo que sitúa a la persona (cuánto
   llevo, cuánto queda). Antes decía "Pareja", que se confunde con la
   pareja de la foto; "duelo" dice lo que es: dos fotos peleando. */
.chip-duelo {
  display: inline-block;
  background: var(--blue);
  color: #fff;
  border-radius: var(--radius-pill);
  padding: 0.45rem 0.95rem;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.puntos { display: flex; gap: 0.28rem; flex-wrap: wrap; }

.punto {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  transition: background 0.2s ease, transform 0.2s ease;
}
.punto--hecho { background: var(--blue); }
.punto--ahora { background: var(--blue); transform: scale(1.35); }

/* "Foto 1 de 2" / "Las dos fotos": información de segundo orden, dónde
   estás DENTRO del duelo. En gris y sin fondo. Antes iba en azul sólido y
   competía con el contador de duelos: cuando dos cosas gritan, no se lee
   ninguna. */
.marca-foto {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--ink-faint);
  font-size: 0.85rem;
  font-weight: 700;
  white-space: nowrap;
}

.chip-puntos { display: inline-flex; gap: 0.25rem; }

.chip-punto {
  width: 0.42rem;
  height: 0.42rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  transition: background 0.2s ease, transform 0.2s ease;
}
.chip-punto--activo { background: var(--ink-faint); transform: scale(1.25); }

/* --------------------------------------------------------------------------
   El escenario: una foto a la vez
   --------------------------------------------------------------------------

   Dos láminas en fila dentro de una tira que se desplaza. Se mueve con
   transform y no cambiando `display`, porque así el arrastre del dedo
   puede seguir al dedo en tiempo real en vez de saltar al soltar.

   object-fit: contain y nunca cover. Se está juzgando el encuadre de cada
   foto: enseñar una versión recortada sería pedir un voto sobre otra cosa.
   El fondo oscuro es el que menos molesta con fotos de atardecer. */

.escenario {
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: #0d0f1c;
  /* `none` y no `pan-y`: el pellizco tiene que llegar a votar.js para
     ampliar la FOTO. Si lo coge el navegador, lo que amplía es la página
     entera, con la cabecera y los botones descolocados, y la persona se
     queda perdida sin saber cómo volver. Se puede poner `none` sin miedo
     porque esta pantalla no scrollea. */
  touch-action: none;
}

/* La tira lleva las dos láminas y es lo ÚNICO que se mueve al deslizar.
   La cabecera se queda quieta: es información sobre dónde estás, no parte
   de lo que estás mirando. */
.tira {
  display: flex;
  width: 200%;
  height: 100%;
  will-change: transform;
}

/* Centrado con flex y no con grid, y no es indiferente.
   ----------------------------------------------------------------------
   Con `display: grid; place-items: center`, el `height: 100%` de la foto
   se resuelve contra la FILA, que es de tamaño automático: si la foto es
   más alta que el hueco, la fila crece hasta la foto y el 100 % pasa a
   valer 1080 px en vez de los 720 de la pantalla. Resultado medido: la
   foto apaisada llenaba el alto y la vertical se quedaba a tamaño
   natural, desbordando. Con flex, el porcentaje se resuelve contra la
   altura del contenedor, que sí es definida, y las dos se comportan
   igual. */
.lamina {
  width: 50%;
  height: 100%;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.lamina img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transform-origin: center center;
  will-change: transform;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  /* Una <img> es arrastrable por defecto, y al deslizarla con el ratón el
     navegador arrancaba SU arrastre (el de soltar la imagen en otra
     ventana) y mandaba un `pointercancel` que abortaba el gesto a media
     pasada. Se vio en la traza de una prueba. */
  -webkit-user-drag: none;
}

/* ==========================================================================
   La vista de arranque: las dos fotos a pantalla completa, con el eje
   ==========================================================================

   Aquí está la idea entera del duelo. Al abrir, cada foto se estira hasta
   ocupar TODO EL ALTO de la pantalla y se corta por los lados, así que lo
   que se ve es media foto, el eje en el centro, y media foto. Grandes, no
   en miniatura.

   Se probó antes a encoger las dos para que cupieran enteras, y estaba
   mal: se veían pequeñas y el duelo perdía toda la fuerza. Lo que importa
   al abrir no es ver las fotos completas, es entender de un golpe que hay
   DOS y que hay que elegir. Verlas completas viene después, al centrar una.

   `height: 100%` + `width: auto` + `max-width: none` es literalmente "toda
   la foto a lo alto, a lo ancho lo que quepa". */
.tira--eje .lamina img {
  flex: 0 0 auto;
  height: 100%;
  width: auto;
  max-width: none;
  max-height: none;
  object-fit: cover;
}

/* El eje. Una línea clara justo en el centro de la pantalla, que es donde
   se tocan las dos láminas. */
.tira--eje .lamina + .lamina::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: #fff;
  z-index: 3;
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.5);
}

/* ==========================================================================
   Los botones de cada foto
   ==========================================================================

   Van ENCIMA de la foto, flotando, y no debajo. Dos razones y las dos
   pesan: la foto se queda con todo el alto disponible (que es lo único
   que se está mirando), y el botón viaja con su foto al deslizar, así que
   no hay duda de cuál se está votando. Un botón fijo en el pie de la
   pantalla no pertenece a ninguna de las dos. */
.btn-lamina {
  position: absolute;
  left: 50%;
  bottom: 1.1rem;
  transform: translateX(-50%);
  z-index: 4;
  border: 0;
  border-radius: var(--radius-pill);
  padding: 0.85rem 1.9rem;
  font: inherit;
  font-size: 1.02rem;
  font-weight: 800;
  background: var(--blue);
  color: #fff;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.5);
  transition: opacity 0.2s ease, background 0.15s ease;
  min-height: 50px;
}
.btn-lamina:hover { background: var(--blue-dark); }
.btn-lamina:active { transform: translateX(-50%) scale(0.98); }

/* En la vista de las dos, los botones se apagan: desde ahí lo que toca es
   mirar una de cerca, no votar a medio ver. Con opacidad y no con display,
   para que no salte nada al centrar. */
.tira--eje .btn-lamina { opacity: 0; pointer-events: none; }

/* La lupa cambia de "+" a "−" escondiendo una raya, NO reescribiendo el
   SVG desde JavaScript.
   ----------------------------------------------------------------------
   La primera versión hacía `svg.innerHTML = ...` en cada repintado, y eso
   destruía el <path> que acababa de recibir el `pointerdown`. Sin ese
   nodo, el navegador no puede resolver a qué elemento pertenece el clic y
   sencillamente NO emite el evento: la lupa se podía pulsar y no pasaba
   absolutamente nada, sin error ni traza. Se vio en una prueba que
   registraba pointerdown, pointerup y click: llegaban los dos primeros y
   el tercero no aparecía nunca.

   Regla general que vale para toda la página: no se reemplaza DOM que
   esté a mitad de un gesto. */
.btn-lupa--menos .lupa-mas { display: none; }

/* La lupa. Existe por una razón concreta: el pellizco de dos dedos no lo
   descubre todo el mundo, y con ratón no existe. Un botón visible lo
   resuelve para todos, y de paso deja claro que ampliar es posible, que
   es lo que de verdad hace falta con las ocho fotos apaisadas. */
.btn-lupa {
  position: absolute;
  top: 0.7rem;
  right: 0.7rem;
  z-index: 4;
  width: 46px;
  height: 46px;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: opacity 0.2s ease, background 0.15s ease;
}
.btn-lupa:hover { background: rgba(0, 0, 0, 0.78); }
.btn-lupa svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.tira--eje .btn-lupa { opacity: 0; pointer-events: none; }

/* Flechas. En el móvil sirven de aviso visual tanto como de control; en
   escritorio no existen. */
.flecha {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: opacity 0.2s ease, background 0.15s ease;
  z-index: 5;
}
.flecha:hover { background: rgba(0, 0, 0, 0.78); }
.flecha svg {
  width: 24px; height: 24px;
  fill: none; stroke: currentColor; stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.flecha--izq { left: 0.5rem; }
.flecha--der { right: 0.5rem; }
.flecha[disabled] { opacity: 0; pointer-events: none; }

.flecha--llama { background: var(--blue); }
.flecha--izq.flecha--llama { animation: llamaIzq 1.5s ease-in-out infinite; }
.flecha--der.flecha--llama { animation: llamaDer 1.5s ease-in-out infinite; }

@keyframes llamaIzq {
  0%, 100% { transform: translateY(-50%) translateX(0); }
  50%      { transform: translateY(-50%) translateX(7px); }
}
@keyframes llamaDer {
  0%, 100% { transform: translateY(-50%) translateX(0); }
  50%      { transform: translateY(-50%) translateX(-7px); }
}

/* El velo de carga. Aparece con retraso a propósito: si las fotos llegan
   rápido (que es lo normal) no llega a verse, y así no hay un parpadeo
   gris entre duelo y duelo. Solo se enseña cuando de verdad se está
   esperando. */
.cargando {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: grid;
  place-items: center;
  background: rgba(13, 15, 28, 0.72);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
body[data-cargando] .cargando {
  opacity: 1;
  transition-delay: 0.45s;
}

/* El corazón del voto */
.corazon {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  opacity: 0;
}
.corazon svg {
  width: 40%;
  max-width: 190px;
  fill: var(--corazon);
  filter: drop-shadow(0 10px 30px rgba(0, 0, 0, 0.45));
}
.corazon--late { animation: late 700ms cubic-bezier(0.2, 0.9, 0.3, 1) forwards; }

@keyframes late {
  0%   { opacity: 0; transform: scale(0.3); }
  30%  { opacity: 1; transform: scale(1.12); }
  45%  { opacity: 1; transform: scale(0.96); }
  60%  { opacity: 1; transform: scale(1.02); }
  100% { opacity: 0; transform: scale(1.25); }
}

.pista {
  margin: 0;
  text-align: center;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink-faint);
  min-height: 1.3em;
}

.pista--aviso { color: var(--blue); font-weight: 700; }
.pista--girar { color: var(--ink-dim); }

/* Pantallas anchas: las dos fotos a la vez y se acabó el carrusel.
   ----------------------------------------------------------------------
   En un monitor caben las dos con tamaño de sobra, así que deslizar sería
   una molestia inventada. Cada foto lleva su propio botón encima y no hay
   flechas, ni eje que centrar, ni botón de abajo.

   El !important sobre transform es lo único feo de aquí, y está para
   ganarle al estilo en línea que escribe votar.js al arrastrar: si alguien
   estrecha la ventana, arrastra, y la vuelve a ensanchar, sin esto la tira
   se quedaría desplazada a la mitad. */
@media (min-width: 760px) {
  /* La pantalla del duelo se sale del ancho de las demás.
     820 px es la medida de una columna de texto y está bien para las
     pantallas que se leen. Aquí no se lee nada: se comparan dos
     fotografías, y el ancho es EL recurso. En un portátil normal esto
     pasa de 820 px a los 96 % de la ventana, que en una pantalla de
     1440 px son 560 px más de foto. */
  .pantalla--duelo { max-width: min(1480px, 96vw); }

  /* La cabecera de la portada, con techo.
     Con la proporción del original (1200x765) sobre los 820 px de la
     columna salen 523 px de alto, y en un portátil de 768 px eso es la
     pantalla entera: se ve la foto y nada más. El techo la recorta por
     arriba (object-position tirando hacia abajo) para conservar la franja
     oscura donde va el titular, que es la parte que no se puede perder. */
  .portada img {
    max-height: 52vh;
    object-position: center 68%;
  }

  .escenario { background: transparent; }
  .tira {
    width: 100% !important;
    transform: none !important;
    gap: 0.9rem;
  }
  /* El ancho de cada lámina lo escribe votar.js en --frac, según la
     proporción de la foto que le toca (ver repartoDeAncho). El 50 % de
     respaldo es lo que se ve si el JavaScript no ha llegado a correr.

     El `- 0.45rem` es medio hueco de los 0,9rem de `gap`. Sin restarlo,
     dos anchos del 50 % más el hueco suman más del 100 % y la segunda
     lámina se sale del escenario. Estaba así de antes y se notaba poco
     porque el escenario recorta; con repartos desiguales se notaría. */
  .lamina {
    flex: 0 0 calc(var(--frac, 50%) - 0.45rem);
    width: auto;
    background: #0d0f1c;
    border-radius: var(--radius-lg);
  }
  /* En escritorio no hay vista de eje: las dos caben a la vez y enteras.
     Así que se deshacen las reglas del eje pase lo que pase con la clase. */
  .tira--eje .lamina img {
    height: auto;
    width: auto;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
  }
  .tira--eje .lamina + .lamina::before { display: none; }
  .tira--eje .btn-lamina,
  .tira--eje .btn-lupa { opacity: 1; pointer-events: auto; }
  .flecha { display: none; }
  .marca-foto { display: none; }
}

/* --------------------------------------------------------------------------
   Móvil y fotos apaisadas: dónde va el zoom y dónde NO
   --------------------------------------------------------------------------

   Aquí hubo una regla que ampliaba las apaisadas cuando estaban centradas
   (`transform: scale(var(--escala-ap))`, entre 1,0 y 1,35 según lo achatada
   que fuera cada una). Se quitó el 18 de agosto y conviene dejar escrito
   por qué, porque la idea suena bien y volverá a tentar a alguien.

   El fallo no era el tamaño, era que NO SE PODÍA DESHACER. La pantalla del
   duelo tiene dos momentos:

     1. Las DOS fotos a la vez, con el eje en el centro (`.tira--eje`). Ahí
        cada una se estira a todo el alto y se recorta por los lados, que es
        el golpe de vista: hay dos, elige. Esa es la mirada "con zoom", y se
        queda como está.
     2. UNA foto centrada, después de tocarla o deslizar. Ese es el momento
        de JUZGARLA, y para juzgar una fotografía hay que verla entera:
        `object-fit: contain`, completa, con su encuadre.

   La regla de la escala se aplicaba en el momento 2, o sea justo donde no
   tocaba. Y como el suelo del zoom manual es 1,0 (ver zoomA en votar.js),
   quitar el zoom devolvía a 1,0... que con esta regla puesta volvía a ser
   1,22. Resultado: la apaisada se quedaba recortada para siempre y no había
   forma de verla completa. Se pedía "un poco de zoom y que luego el usuario
   amplíe O REDUZCA", y lo de reducir no funcionaba.

   Así que el orden ahora es: primero el vistazo recortado de las dos, y al
   tocar una, la foto entera. Desde ahí la lupa, la rueda, el pellizco y el
   doble toque amplían lo que haga falta, y siempre se puede volver.

   Que una apaisada completa en un móvil vertical se vea pequeña no es un
   fallo que se pueda arreglar, es geometría: en una pantalla alta y estrecha
   una foto ancha y baja ocupa poco. Lo único que se puede elegir es si se
   ve pequeña o se ve recortada, y para decidir un concurso de fotografía la
   respuesta es pequeña, con la lupa a un toque.

   El arreglo de verdad para las apaisadas está en escritorio, donde sí hay
   ancho que repartir: ver .lamina y repartoDeAncho(). */

/* Móvil en horizontal: se recorta lo prescindible para que la foto no se
   quede en un sello. */
@media (max-height: 460px) {
  .pista { display: none; }
  .pantalla { padding-top: calc(var(--safe-top) + 0.35rem); }
  .btn { min-height: 44px; padding: 0.6rem 1.2rem; }
}

/* --------------------------------------------------------------------------
   El medidor (solo al final de la tanda)
   -------------------------------------------------------------------------- */

.medidor {
  background: var(--surface);
  border-radius: var(--radius-md);
  padding: 0.9rem 1rem;
  margin-top: 1rem;
  text-align: left;
}

.medidor-fila {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.medidor-titulo {
  font-size: 0.92rem;
  font-weight: 800;
  color: var(--ink);
}

.medidor-cifra {
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--blue);
  font-variant-numeric: tabular-nums;
}

.barra {
  height: 9px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  overflow: hidden;
}

.barra-relleno {
  height: 100%;
  width: 0%;
  border-radius: var(--radius-pill);
  background: var(--blue);
  transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

.barra-relleno--listo { background: var(--success); }

/* El texto que se lee al terminar de votar. Es lo único que mucha gente
   va a leer de toda la página, así que va a tamaño de lectura de verdad y
   no a tamaño de pie de foto. */
.texto-final {
  margin: 1rem 0 0;
  font-size: 1.02rem;
  line-height: 1.55;
  color: var(--ink-dim);
  text-align: left;
}
.texto-final strong { color: var(--ink); font-weight: 700; }

.aviso-texto--plazo { border-top: 1px solid var(--border); padding-top: 0.7rem; }

.medidor-pie {
  margin: 0.6rem 0 0;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--ink-dim);
}

/* --------------------------------------------------------------------------
   Final
   -------------------------------------------------------------------------- */

.spinner {
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 3px solid var(--surface-2);
  border-top-color: var(--blue);
  animation: gira 0.8s linear infinite;
}
.spinner--centro { margin: 3rem auto; }
@keyframes gira { to { transform: rotate(360deg); } }

.tic {
  width: 68px; height: 68px;
  border-radius: 50%;
  background: var(--blue-soft);
  display: grid; place-items: center;
  margin: 0.5rem auto 0.8rem;
}
.tic svg {
  width: 33px; height: 33px;
  fill: none; stroke: var(--blue); stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  /* El corazón es la confirmación de que el voto ha entrado, así que aquí
     no puede desaparecer del todo: se queda quieto y visible su instante. */
  .corazon--late { animation: none; opacity: 1; }
}

/* Con la foto ampliada SE PUEDE VOTAR, y el botón sigue entero.
   ----------------------------------------------------------------------
   La versión anterior lo apagaba, con el razonamiento de que un pellizco
   no acabara en un voto sin querer. Era un razonamiento malo: para votar
   hay que pulsar un botón, no hacer un gesto, así que el accidente que se
   quería evitar no existía. Y el efecto real era el contrario del que
   busca la pantalla: te acercas a mirar el detalle que te va a decidir,
   te decides, y justo ahí la app te obliga a deshacer el zoom antes de
   dejarte votar. Absurdo.

   Se queda un poco más transparente para no tapar la foto ampliada, que
   es lo que se está mirando, pero funciona igual. */
.tira--zoom .btn-lamina { opacity: 0.88; }


/* ==========================================================================
   El preparador
   ==========================================================================

   Tapa la pantalla entera mientras se trae lo que hace falta para el
   primer duelo. Cubrirlo todo es el objetivo, no un efecto secundario: en
   una conexión lenta, la pantalla se quedaba tal cual sin responder y lo
   natural era volver a pulsar el botón, o concluir que la página estaba
   rota.

   La barra es de bytes de verdad, no un adorno que se mueve solo: votar.js
   lee las fotos con fetch y va contando lo que llega. Una barra falsa que
   avanza sola es peor que ninguna, porque miente sobre cuánto queda. */
.preparador {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.96);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  /* Se come todos los toques, incluido el del botón que acaba de pulsarse. */
  touch-action: none;
}

.preparador[hidden] { display: none !important; }

.preparador-caja { text-align: center; padding: 2rem 1.5rem; max-width: 340px; }

.preparador-aro { position: relative; width: 120px; height: 120px; margin: 0 auto 1.2rem; }
.preparador-aro svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.preparador-aro circle { fill: none; stroke-width: 9; stroke-linecap: round; }
.aro-fondo { stroke: var(--surface-2); }
.aro-relleno {
  stroke: var(--blue);
  /* 2·π·52 ≈ 326,7: la circunferencia entera. El relleno se dibuja
     moviendo el desfase del trazo discontinuo, que es la forma de hacer
     un aro de progreso sin una sola imagen. */
  stroke-dasharray: 326.7;
  stroke-dashoffset: 326.7;
  transition: stroke-dashoffset 0.25s ease-out;
}

/* Modo "no sé cuánto queda".
   ----------------------------------------------------------------------
   Al enviar los votos no hay bytes que contar: es una petición pequeña y
   lo que se está esperando es al servidor. Así que el aro gira en vez de
   llenarse, y no se enseña ningún porcentaje. Inventar un "43 %" ahí
   sería exactamente la barra mentirosa que se quería evitar. */
.preparador--girando .aro-relleno {
  stroke-dasharray: 90 240;
  stroke-dashoffset: 0;
  transition: none;
  animation: giraAro 1.1s linear infinite;
  transform-origin: 60px 60px;
}
.preparador--girando .preparador-cifra { display: none; }

@keyframes giraAro {
  to { transform: rotate(360deg); }
}

.preparador-cifra {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.preparador-texto {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ink);
}

.preparador-nota {
  margin: 0.5rem 0 0;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--ink-faint);
  min-height: 2.6em;
}
