/**
 * styles.css — Estética cómic/Spider-Man, mobile-first estricto.
 * Pensado para Chrome/MIUI en un Xiaomi Redmi Note 14 (uso a una mano).
 */

/* ── Variables ─────────────────────────────────────────── */
:root {
  --sm-red: #e23636;
  --sm-red-dark: #a11f1f;
  --sm-blue: #1d4e9e;
  --sm-blue-dark: #0f2e63;
  --sm-dark: #0b0e1a;
  --sm-cream: #f5f1e6;
  --sm-yellow: #ffd23f;

  --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);

  --vh: 100dvh; /* sustituido a 100vh vía fallback abajo */
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  /* 100dvh probó ser inconsistente en Chrome/MIUI real (bug encontrado en
     Redmi Note 14 y Samsung A56, 2026-07-26): sin scroll en la página, la
     barra de direcciones no siempre dispara el recálculo dinámico y el
     layout terminaba usando la altura "grande" (barra oculta), cortando
     contenido cuando la barra SÍ está visible. 100svh (small viewport
     height) fija el peor caso posible desde el inicio, sin depender de
     ningún recálculo — con 100vh como fallback para navegadores que no
     soportan ninguna de las dos unidades modernas. */
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--sm-dark);
  color: var(--sm-cream);
  font-family: 'Luckiest Guy', 'Segoe UI', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

body {
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  position: relative;
}

/* Fondo de telaraña sutil, reutilizable en toda la app (SVG inline, sin peticiones extra) */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.08;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200' viewBox='0 0 200 200'%3E%3Cg fill='none' stroke='%23F5F1E6' stroke-width='1'%3E%3Ccircle cx='100' cy='100' r='20'/%3E%3Ccircle cx='100' cy='100' r='45'/%3E%3Ccircle cx='100' cy='100' r='75'/%3E%3Ccircle cx='100' cy='100' r='110'/%3E%3Cline x1='100' y1='100' x2='100' y2='-20'/%3E%3Cline x1='100' y1='100' x2='210' y2='40'/%3E%3Cline x1='100' y1='100' x2='210' y2='160'/%3E%3Cline x1='100' y1='100' x2='100' y2='220'/%3E%3Cline x1='100' y1='100' x2='-10' y2='160'/%3E%3Cline x1='100' y1='100' x2='-10' y2='40'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 320px 320px;
}

button {
  font-family: inherit;
  border: none;
  cursor: pointer;
  touch-action: manipulation;
}

h1, h2 {
  font-family: 'Bangers', Impact, 'Arial Narrow', sans-serif;
  letter-spacing: 0.03em;
  margin: 0;
}

img { max-width: 100%; display: block; }

/* ── Máquina de fases ──────────────────────────────────── */
.phase {
  position: fixed;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease;
  padding: calc(var(--safe-top) + 16px) calc(var(--safe-right) + 16px)
           calc(var(--safe-bottom) + 16px) calc(var(--safe-left) + 16px);
}

body[data-phase="invite"] .phase-invite,
body[data-phase="trailer"] .phase-trailer,
body[data-phase="final"] .phase-final {
  opacity: 1;
  visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
  .phase { transition: none; }
}

/* ═════════════════════════════════════════════════════════
   FASE 1 — Invitación
═════════════════════════════════════════════════════════ */
/* overflow-y: auto como red de seguridad (2026-07-26): si en algún
   dispositivo real con proporción muy distinta el contenido igual no
   entrara por completo en 100svh, queda scrolleable en vez de cortado
   sin remedio. overflow-x se mantiene en hidden porque el layout es
   mobile-first de una sola columna, sin necesidad de scroll horizontal. */
.phase-invite { padding: 0; overflow-x: hidden; overflow-y: auto; }

.hero-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-color: var(--sm-blue-dark); /* color de respaldo si la imagen no carga */
}

.hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(11, 14, 26, 0.55) 0%,
    rgba(11, 14, 26, 0.25) 40%,
    rgba(11, 14, 26, 0.85) 100%
  );
}

/* position: fixed (no absolute): el botón ya no vive dentro de ningún
   .phase, así que su posicionamiento no puede depender de un ancestro de
   fase — debe anclarse siempre al viewport, sin importar data-phase.
   z-index por encima de TODO lo demás en el sitio (incluido .trailer-fade,
   el fundido a negro con z-index:30 más alto de las 3 fases) para que se
   pueda tocar en cualquier momento, incluso durante esa transición. */
.btn-fullscreen-toggle {
  position: fixed;
  top: max(16px, var(--safe-top));
  right: max(16px, var(--safe-right));
  z-index: 40;
  width: 44px;
  height: 44px;
  min-height: 44px;
  padding: 0;
  background: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-fullscreen-toggle:active { transform: scale(0.92); }

.btn-fullscreen-toggle img {
  width: 40px;
  height: 40px;
  display: block;
  pointer-events: none;
}

.invite-content {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  /* padding-bottom generoso a propósito: como el bloque pregunta+botones
     está anclado abajo (justify-content: flex-end), subir este valor sube
     el bloque completo (acercándolo al póster de arriba) y a la vez
     aumenta su margen real respecto al borde inferior/zona de gestos.
     Reducido levemente (2026-07-26, bug real en Redmi Note 14/Samsung
     A56: la pregunta se cortaba abajo con la barra de Chrome visible) para
     dar más margen de seguridad en dispositivos con menos altura visible
     — junto con el fix de 100svh arriba y el overflow-y:auto de
     .phase-invite, no debería depender de esto para verse completa, pero
     menos espacio reservado es menos superficie donde un caso límite
     pueda seguir recortando texto. */
  padding: calc(var(--safe-top) + 20px) calc(var(--safe-right) + 20px)
           calc(var(--safe-bottom) + 72px) calc(var(--safe-left) + 20px);
  gap: 20px;
}

/* Fuera del flujo flex de .invite-content a propósito: así su tamaño no
   empuja ni afecta en absoluto la posición de la pregunta/botones (que
   siguen ancladas abajo igual que antes). Se ubica cerca del botón de
   pantalla completa, con un margen fijo cómodo respecto a él. */
.invite-poster {
  position: absolute;
  top: calc(var(--safe-top) + 90px);
  left: 50%;
  transform: translateX(-50%);
  width: auto;
  height: auto;
  max-width: min(92vw, 380px);
  /* Reducido de 54vh a 50svh (2026-07-26): un poco menos de altura reservada
     para el póster deja más margen real a la pregunta de abajo en
     dispositivos con menos altura visible (barra de Chrome presente). */
  max-height: 54vh;
  max-height: 50svh;
  aspect-ratio: 4 / 5; /* proporción real de assets/img/caratula.webp (960×1200) */
  object-fit: contain;
  background: var(--sm-dark); /* respaldo si el póster no llena el marco */
  border: 4px solid #000;
  border-radius: 10px;
  box-shadow: 5px 5px 0 #000;
}

.invite-question {
  font-size: clamp(2.2rem, 9.5vw, 3.1rem);
  text-align: center;
  color: var(--sm-cream);
  text-shadow: 0 3px 0 var(--sm-red-dark), 0 6px 16px rgba(0, 0, 0, 0.6);
  line-height: 1.15;
  max-width: min(22ch, 90vw);
  white-space: pre-line;
}

.invite-buttons {
  position: relative;
  width: 100%;
  max-width: 420px;
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: center;
  min-height: 76px; /* reserva espacio para que el "No" no cause salto de layout */
}

.btn {
  font-family: 'Bangers', Impact, sans-serif;
  font-size: 1.5rem;
  letter-spacing: 0.05em;
  border-radius: 999px;
  padding: 16px 20px;
  min-height: 56px;
  border: 4px solid #000;
  color: var(--sm-cream);
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.55);
  /* Sombra dura de cómic: bloque sólido desplazado, sin blur (nada de
     rgba difuminado) — ver estado :active para el efecto de "presionado". */
  box-shadow: 5px 5px 0 #000;
  transition: transform 0.15s ease, width 0.4s ease, box-shadow 0.15s ease;
}

/* Al presionar, la sombra "se achica" y el botón se desplaza hacia donde
   apuntaba — simula un botón físico de cómic hundiéndose. */
.btn:active { transform: translate(3px, 3px); box-shadow: 1px 1px 0 #000; }

.btn-yes {
  flex: 1;
  position: relative;
  z-index: 1; /* explícito: debe quedar SIEMPRE por debajo de btn-no.is-evading (ver abajo) */
  background: var(--sm-red);
}

.btn-yes.btn-yes--full {
  flex: none;
  width: 100%;
  animation: pulse 1.4s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.04); }
}

.btn-no {
  background: var(--sm-blue);
  min-width: 96px;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
  will-change: transform;
}

/* Al primer intento de evasión se saca del flujo (JS fija left/top con su
   posición real medida) para poder moverlo libremente por la pantalla.
   z-index explícitamente mayor al de btn-yes (ver arriba): refuerzo para
   que, si algún escenario límite lograra solapar visualmente ambos
   botones pese a la exclusión de área en JS, el tap siempre caiga sobre
   "No" (el que se ve arriba) y nunca sobre "Sí". */
.btn-no.is-evading {
  position: fixed;
  z-index: 8;
}

.btn-no.is-fleeing { transition-duration: 0.28s; }

.btn-no.is-gone {
  opacity: 0;
  pointer-events: none;
  transform: scale(0) !important;
}

.no-taunt {
  position: fixed;
  z-index: 6;
  background: var(--sm-yellow);
  color: var(--sm-dark);
  font-weight: 700;
  font-size: 0.9rem;
  padding: 8px 14px;
  border-radius: 14px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  animation: taunt-pop 1.6s ease forwards;
  white-space: nowrap;
}

@keyframes taunt-pop {
  0% { opacity: 0; transform: translateY(6px) scale(0.9); }
  15% { opacity: 1; transform: translateY(0) scale(1); }
  80% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-6px) scale(0.95); }
}

@keyframes fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Fondo compartido (2026-07-26): mismo hero.jpg + degradado oscuro que la
   Fase 1, reutilizado en tráiler y final para que todo el recorrido se
   sienta como un mismo sitio en vez de pantallas negras/azules sueltas.
   Se aplica como background-image directo (no como div ::before) a
   propósito: en .phase-final, que puede hacer scroll (overflow-y: auto),
   el background-attachment por defecto ("scroll") queda fijo respecto a
   la CAJA del elemento, no respecto a su contenido — si en cambio fuera un
   div hijo con position:absolute, se desplazaría junto con el contenido
   al hacer scroll, dejando el fondo incompleto. El patrón sutil de
   telaraña (body::before) ya se ve detrás de ambas fases sin cambios,
   porque es global (fixed, z-index:0). */
.phase-trailer,
.phase-final {
  background-image: linear-gradient(
    to bottom,
    rgba(11, 14, 26, 0.55) 0%,
    rgba(11, 14, 26, 0.25) 40%,
    rgba(11, 14, 26, 0.85) 100%
  ), url('../assets/img/hero.jpg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-color: var(--sm-dark); /* respaldo si la imagen no carga */
}

/* ═════════════════════════════════════════════════════════
   FASE 2 — Tráiler
   Rotación CSS FIJA: .video-wrap siempre rota 90deg, sin importar la
   orientación real del dispositivo ni el rotation lock del sistema (ver
   nota de decisión en CLAUDE.md). Los controles/overlays son hermanos de
   .video-wrap en el HTML, así que nunca heredan su transform.
═════════════════════════════════════════════════════════ */
.phase-trailer { padding: 0; overflow: hidden; }

.video-wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 100vh;
  height: 100vw;
  transform: translate(-50%, -50%) rotate(90deg);
  transform-origin: center center;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Transparente a propósito (2026-07-26): antes era #000 sólido, lo que
     se veía como un letterbox negro alrededor del video (cuando su aspect
     ratio no llena el contenedor rotado). Ahora deja ver el fondo hero.jpg
     de .phase-trailer detrás, sin heredar la rotación (la transparencia no
     tiene orientación) — misma continuidad visual que el resto del sitio. */
  background: transparent;
}

#trailer-video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: transparent; /* ver comentario en .video-wrap arriba */
}

.video-loading {
  position: fixed;
  inset: 0;
  z-index: 15;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: rgba(0, 0, 0, 0.75);
  color: var(--sm-cream);
}

/* El atributo [hidden] por sí solo NO alcanza aquí: como origen "author"
   .video-loading{display:flex} le gana en cascada a la regla [hidden] del
   navegador (origen "user-agent"), sin importar la especificidad. Sin esta
   regla explícita, videoLoading.hidden = true no oculta nada visualmente. */
.video-loading[hidden] {
  display: none;
}

.spinner {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 5px solid rgba(245, 241, 230, 0.25);
  border-top-color: var(--sm-red);
  animation: spin 0.9s linear infinite;
}

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

.btn-unmute {
  position: fixed;
  bottom: calc(max(24px, var(--safe-bottom)) + 72px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  background: var(--sm-yellow);
  color: var(--sm-dark);
  font-weight: 700;
  border-radius: 999px;
  padding: 12px 22px;
  min-height: 48px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
}

/* Franja fija en la parte inferior de la pantalla FÍSICA (no del video
   rotado): elemento normal del flujo portrait, superpuesto sobre el
   video, siempre legible en vertical. Visible durante toda la
   reproducción, no solo al terminar o en error. */
.trailer-controls {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  justify-content: center;
  gap: 14px;
  padding: 14px 16px calc(14px + var(--safe-bottom));
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0));
}

.btn-trailer {
  font-family: 'Bangers', Impact, sans-serif;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  border-radius: 999px;
  padding: 12px 22px;
  min-height: 48px;
  background: rgba(0, 0, 0, 0.55);
  color: var(--sm-cream);
  border: 2px solid rgba(245, 241, 230, 0.4);
  transition: transform 0.15s ease;
}

.btn-trailer:active { transform: translateY(2px); }

/* Fundido a negro al entrar a la fase "trailer" (~450-500ms en total, ver
   CONFIG.behavior.trailerFadeHoldMs): cubre todo, incluidos los controles
   fijos, y se desvanece revelando el tráiler ya en marcha, en vez de
   saltar de golpe. pointer-events:none para que nunca bloquee nada una
   vez que empieza a desvanecerse. */
.trailer-fade {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: #000;
  opacity: 1;
  pointer-events: none;
  transition: opacity 450ms ease;
}

.trailer-fade--out {
  opacity: 0;
}

/* ═════════════════════════════════════════════════════════
   FASE 3 — Sección final
═════════════════════════════════════════════════════════ */
.phase-final {
  /* El fondo (hero.jpg + degradado) está definido arriba, compartido con
     .phase-trailer — ver comentario en esa regla. */
  overflow-y: auto;
  align-items: flex-start;
}

.final-content {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 440px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  padding-top: 12px;
  padding-bottom: 24px;
}

/* "Colgando" de la esquina superior (2026-07-27), cerca del eyebrow sin
   pisar sus letras: position:absolute relativo a .final-content (que ya
   es position:relative), corrido a la izquierda y con un top negativo
   para que se sienta como si el hilo de telaraña siguiera de largo hacia
   arriba, fuera del marco visible. Ligeramente rotado para que no se vea
   perfectamente vertical/estático. Como .phase-final puede hacer scroll,
   esto se desplaza junto con el resto del contenido (no queda "fijo" en
   la esquina de la pantalla) — es lo esperado, es decoración de esta
   sección, no un elemento persistente como el botón de fullscreen. */
.final-hanging-spider {
  position: absolute;
  top: -14px;
  left: 0;
  /* Agrandado (2026-07-28, antes 52px, se perdía contra la textura del
     fondo). Sigue sin pisar el eyebrow porque este último está centrado y
     bastante más angosto que el ancho de .final-content, así que el
     flanco izquierdo queda libre — si llega a solaparse con la esquina
     del boleto de abajo es intencional (efecto "colgando encima de"), no
     con el texto. */
  width: 80px;
  height: auto;
  transform: rotate(-8deg);
  z-index: 1;
  pointer-events: none;
  animation: fade-in 0.6s ease both;
}

.final-eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 0.8rem;
  color: var(--sm-yellow);
  font-weight: 700;
  margin: 0;
  /* Sombra agregada (2026-07-26): sobre hero.jpg (antes fondo azul plano)
     hace falta para que siga siendo legible en las zonas más claras de la foto. */
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.7);
  animation: fade-in 0.5s ease both;
}

/* Agrandado (2026-07-26, boletos.png real reemplazó al placeholder
   genérico): el placeholder era un dibujo simple pensado para leerse
   chico; el boleto real de Cinex tiene texto de confirmación (fecha,
   sala, localizador) que necesita bastante más espacio para ser legible,
   no solo que el QR sea escaneable. No hizo falta max-height aparte: con
   aspect-ratio fijo en .tickets-img (ver abajo), la altura del marco
   queda determinada por su ancho.
   Ancho en % y no en vw (fix 2026-07-27, bug real en dispositivo: el
   marco tocaba los bordes de la pantalla): vw se calcula sobre el
   viewport completo, ignorando el padding lateral de .phase (16px+
   safe-area) — con 94vw ese padding + el ancho del marco sumaban más
   que el 100% del ancho real, así que el marco terminaba más pegado al
   borde de lo esperado (o directamente desbordando). % en cambio se
   calcula sobre el ancho ya disponible dentro de .final-content (que sí
   descuenta ese padding), garantizando el margen lateral sin importar
   el safe-area del dispositivo. */
.tickets-frame {
  width: 88%;
  max-width: 380px;
  border-radius: 12px;
  overflow: hidden;
  transform: rotate(-2deg);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5), 0 0 0 6px var(--sm-cream), 0 0 0 9px var(--sm-red);
  margin: 10px 0;
  animation: fade-in 0.6s ease 0.1s both;
}

.tickets-img {
  width: 100%;
  /* Proporción real de assets/img/boletos.png (932×766), no la 3/2
     genérica pensada para el placeholder anterior. */
  aspect-ratio: 932 / 766;
  /* contain (no cover): boletos.png es una captura de confirmación real
     con texto y código QR — recortar cualquier borde (como hacía cover)
     podía cortar el localizador o parte del texto. contain nunca recorta
     ni distorsiona, a costa de poder dejar una franja de fondo si el
     aspect-ratio de arriba no calza exacto con el real (no debería, ya
     que es el mismo). */
  object-fit: contain;
  background: var(--sm-blue-dark);
}

/* Fila que agrupa el título con sus dos arañitas (2026-07-27). La
   animación de entrada vive acá (antes estaba en .final-date sola) para
   que el título y los íconos aparezcan juntos como un solo bloque. */
.final-date-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(6px, 2.5vw, 14px);
  animation: fade-in 0.6s ease 0.2s both;
}

/* Agrandadas (2026-07-28, antes clamp(24px, 7vw, 38px)): junto al título
   en Bangers (2.4-3.2rem, ~38-51px de alto) se veían diminutas. El nuevo
   rango crece más rápido con el viewport (14vw) para tener presencia real
   incluso en pantallas angostas, sin volverse gigante en las anchas. */
.final-date-spider {
  width: clamp(42px, 14vw, 60px);
  height: auto;
  flex-shrink: 0;
}

/* Mismo archivo que el ícono de la izquierda (ver index.html) — se
   refleja por CSS en vez de pedir un segundo asset. */
.final-date-spider--right {
  transform: scaleX(-1);
}

.final-date {
  font-size: clamp(2.4rem, 12vw, 3.2rem);
  color: var(--sm-cream);
  /* Mismos valores EXACTOS que .invite-question (2026-07-26, ajuste tras
     comparar peso visual): esa regla no usa text-stroke — el "contorno"
     grueso que se percibe ahí viene de combinar el offset duro en
     --sm-red-dark con un halo oscuro difuminado (16px de blur) alrededor
     de cada letra. Para que este texto tenga el mismo impacto visual que
     la pregunta principal, hay que copiar ambas capas tal cual, no solo
     el color. */
  text-shadow: 0 3px 0 var(--sm-red-dark), 0 6px 16px rgba(0, 0, 0, 0.6);
}

.final-lines {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
  /* width completo a propósito (2026-07-27): .final-line ahora mide menos
     que el 100% (ver abajo) para poder alternarse a izquierda/derecha —
     eso solo se nota si el contenedor que las alinea (este) ocupa todo el
     ancho disponible. Sin este width, .final-lines se encogería a su
     contenido (por el align-items:center de .final-content) y no
     quedaría espacio real para desplazar las tarjetas. */
  width: 100%;
}

.final-line {
  font-family: system-ui, sans-serif;
  font-size: 1.05rem;
  color: var(--sm-cream);
  /* Fondo más oscuro/opaco (2026-07-26, antes rgba(245,241,230,0.08)):
     sobre un fondo plano casi cualquier opacidad alcanzaba, pero sobre
     hero.jpg (foto) hace falta una tarjeta más sólida para que el texto
     siga siendo legible en cualquier zona de la imagen. */
  background: rgba(11, 14, 26, 0.6);
  border: 1px solid rgba(245, 241, 230, 0.2);
  border-radius: 14px;
  padding: 12px 18px;
  /* Menos del 100% + align-self alternado abajo (2026-07-27): efecto
     "mensajes de chat", separando visualmente una tarjeta de la
     siguiente. width (no max-width) a propósito, para que el hueco que
     deja quede siempre del mismo lado sin importar cuánto texto tenga
     cada frase. */
  width: 82%;
  align-self: flex-start;
  /* card-in (no fade-in compartido): mismo fundido que el resto de la
     fase, pero con un slide-up más marcado (15px en vez de los 6px de
     fade-in) para que cada tarjeta se sienta con más vida al aparecer —
     pedido específico para estas tarjetas, no para el resto de la fase. */
  animation: card-in 0.5s ease both;
}

@keyframes card-in {
  from { opacity: 0; transform: translateY(15px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Impares a la izquierda (default de arriba), pares a la derecha. */
.final-line:nth-child(even) { align-self: flex-end; }

.final-line:nth-child(1) { animation-delay: 0.3s; }
.final-line:nth-child(2) { animation-delay: 0.45s; }
.final-line:nth-child(3) { animation-delay: 0.6s; }
.final-line:nth-child(4) { animation-delay: 0.75s; }
.final-line:nth-child(5) { animation-delay: 0.9s; }

.final-footer {
  margin-top: 16px;
  font-family: 'Bangers', Impact, sans-serif;
  font-size: 1.2rem;
  color: var(--sm-red);
  /* Sombra agregada (2026-07-26): mismo motivo que .final-eyebrow arriba. */
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.55);
  animation: fade-in 0.6s ease 0.8s both;
}
