/* Motor visual compartido por las invitaciones demo.
   Cada demo sólo cambia los tokens de tema (temas.css). Depende de
   tokens.css + base.css de la landing, así la marca y el producto
   comparten el mismo sistema. */

:root {
  --inv-fondo:    var(--c-blanco);
  --inv-fondo-alt:var(--c-beige);
  --inv-texto:    var(--c-negro);
  --inv-suave:    var(--texto-suave);
  --inv-acento:   var(--c-oro);
  --inv-linea:    rgba(18,16,14,.12);
  --inv-display:  var(--f-display);
  --inv-radio:    var(--r-lg);
}

body.inv {
  background: var(--inv-fondo);
  color: var(--inv-texto);
}
.inv .contenedor { max-width: 46rem; }

/* ── Barra BONV: la demo siempre sabe volver al estudio ───────── */
.inv-barra {
  position: fixed;
  inset: auto 0 0;
  z-index: 80;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  padding: .7rem clamp(1rem, 4vw, 1.6rem);
  padding-bottom: max(.7rem, env(safe-area-inset-bottom));
  background: rgba(18,16,14,.9);
  backdrop-filter: blur(12px);
  color: var(--c-blanco);
  font-size: var(--t-xs);
  letter-spacing: .1em;
  text-transform: uppercase;
  transform: translateY(110%);
  transition: transform var(--d-media) var(--e-suave);
}
.inv-barra.es-visible { transform: none; }
.inv-barra__marca { display: flex; align-items: center; gap: .55rem; }
.inv-barra__marca b { font-family: var(--inv-display); letter-spacing: .18em; font-weight: 400; }
.inv-barra__punto { width: 5px; height: 5px; border-radius: 50%; background: var(--c-oro-claro); flex: none; }
.inv-barra__cta { color: var(--c-oro-claro); white-space: nowrap; }
.inv-barra__cta:hover { color: var(--c-blanco); }
/* La barra no debe tapar el final del contenido. */
.inv main { padding-bottom: 5rem; }

/* ── Portada: se toca para entrar ─────────────────────────────── */
.portada {
  position: fixed; inset: 0;
  z-index: 200;
  display: grid; place-items: center;
  text-align: center;
  padding: 2rem;
  background: var(--portada-fondo, var(--inv-fondo-alt));
  color: var(--portada-texto, var(--inv-texto));
  cursor: pointer;
  /* El desvanecido espera a que termine la animación de apertura de cada
     tema; --portada-retraso lo fija cada variante. */
  transition: opacity .9s var(--e-suave) var(--portada-retraso, 0s),
              visibility .9s var(--portada-retraso, 0s);
}
.portada.es-abierta { opacity: 0; visibility: hidden; pointer-events: none; }
.portada__marco {
  position: absolute; inset: clamp(.9rem, 3vw, 2rem);
  border: 1px solid currentColor;
  opacity: .3;
  pointer-events: none;
}
.portada__caja { display: grid; gap: 1rem; justify-items: center; max-width: 26rem; }
.portada__eyebrow { font-size: var(--t-xs); letter-spacing: .35em; text-transform: uppercase; opacity: .75; }
.portada__nombre {
  font-family: var(--inv-display);
  font-size: clamp(2.8rem, 1.6rem + 8vw, 5rem);
  line-height: 1;
}
.portada__sub { font-size: var(--t-sm); letter-spacing: .22em; text-transform: uppercase; opacity: .8; }
.portada__linea { width: 3.5rem; height: 1px; background: currentColor; opacity: .45; }
.portada__hint {
  margin-top: 1.5rem;
  font-size: var(--t-xs); letter-spacing: .24em; text-transform: uppercase;
  opacity: .7;
  animation: latir 2.6s var(--e-suave) infinite;
}
@keyframes latir { 0%,100% { opacity: .35; } 50% { opacity: .95; } }

/* ══ APERTURAS ═════════════════════════════════════════════════════
   Tres formas de abrir la portada. Todas se disparan con la misma clase
   .es-abierta que pone invitacion.js, así el motor no sabe nada de esto.  */

/* ── 1. Libro: la tapa gira sobre el lomo izquierdo ───────────── */
.portada--libro {
  --portada-retraso: .95s;
  perspective: 2000px;
  perspective-origin: 0% 50%;
  padding: 0;
  /* Debajo de la tapa quedan las páginas: es lo que se ve al girar. */
  background: var(--libro-paginas, #fbf6f1);
}
.portada__paginas {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(0,0,0,.16), rgba(0,0,0,.02) 3.5rem, transparent 7rem),
    var(--libro-paginas, #fbf6f1);
}
.portada__paginas::after {
  /* Canto de las hojas apiladas. */
  content: "";
  position: absolute; inset: 0 0 0 .35rem;
  background: repeating-linear-gradient(90deg, rgba(0,0,0,.05) 0 1px, transparent 1px 5px);
  opacity: .5;
}
.portada__tapa {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  text-align: center;
  padding: 2rem;
  background: var(--portada-fondo);
  color: var(--portada-texto);
  transform-origin: left center;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  box-shadow: 0 0 60px -10px rgba(0,0,0,.35);
  transition: transform 1.5s cubic-bezier(.62, .02, .22, 1);
}
.portada--libro.es-abierta .portada__tapa { transform: rotateY(-174deg); }
.portada__lomo {
  position: absolute; inset: 0 auto 0 0;
  width: 3.4rem;
  background: linear-gradient(90deg, rgba(0,0,0,.26), rgba(0,0,0,.05) 55%, transparent);
  pointer-events: none;
}
.portada__lomo::after {
  content: "";
  position: absolute; inset: 0 auto 0 .55rem;
  width: 1px;
  background: currentColor;
  opacity: .28;
}

/* ── 2. Sobre: se rompe el sello y se abre la solapa ──────────── */
.portada--sobre {
  --portada-retraso: 1.15s;
  /* Tinta oscura: el texto del hint va sobre papel kraft, no sobre el
     degradado claro del tema. */
  --portada-texto: #4a3419;
  padding: 0;
  overflow: hidden;
}
/* El sobre ocupa toda la pantalla: los pliegues van de esquina a centro. */
.sobre {
  position: absolute;
  inset: 0;
  perspective: 2200px;
}
/* Papel kraft: base + fibra fina, sin una sola imagen. */
.sobre__cara {
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(58deg, rgba(120, 88, 48, .05) 0 2px, transparent 2px 5px),
    radial-gradient(ellipse at 30% 22%, #d3b891, #bb9a6d 62%, #a1805a);
  /* A pantalla completa el papel necesita viñeta o se ve plano. */
  box-shadow: inset 0 0 clamp(6rem, 22vw, 14rem) rgba(72, 50, 24, .34);
}
.sobre__pliegue {
  position: absolute; inset: 0;
  /* Cada solapa trasera es un triángulo hacia el centro, como en un sobre
     real: el sello es justo lo que las mantiene unidas. */
  background: rgba(0, 0, 0, .05);
  /* drop-shadow sigue el contorno del clip-path, así que dibuja la costura
     sobre la diagonal; un box-shadow interior se perdería al recortar. */
  filter: drop-shadow(0 0 1px rgba(84, 60, 32, .55));
}
.sobre__pliegue--izq   { clip-path: polygon(0 0, 50% 50%, 0 100%); background: rgba(0,0,0,.075); }
.sobre__pliegue--der   { clip-path: polygon(100% 0, 50% 50%, 100% 100%); background: rgba(0,0,0,.045); }
.sobre__pliegue--abajo { clip-path: polygon(0 100%, 50% 50%, 100% 100%); background: rgba(255,255,255,.07); }

.sobre__solapa {
  position: absolute; inset: 0;
  clip-path: polygon(0 0, 50% 50%, 100% 0);
  background:
    repeating-linear-gradient(58deg, rgba(120, 88, 48, .05) 0 2px, transparent 2px 5px),
    linear-gradient(180deg, #e0c8a2, #c9a97c 78%, #b28f62);
  filter: drop-shadow(0 1px 2px rgba(84, 60, 32, .5));
  transform-origin: top center;
  transform-style: preserve-3d;
  transition: transform 1.1s cubic-bezier(.5, 0, .25, 1) .28s;
}
.portada--sobre.es-abierta .sobre__solapa { transform: rotateX(-168deg); }

/* Sello de cera: borde irregular + luz alta arriba a la izquierda. */
.sobre__sello {
  position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  z-index: 2;
  width: clamp(16rem, 62vw, 22rem);
  aspect-ratio: 1;
  display: grid; place-items: center;
  /* Borde deliberadamente irregular: la cera derramada nunca es un círculo. */
  border-radius: 44% 56% 39% 61% / 57% 41% 59% 43%;
  background:
    /* brillo especular */
    radial-gradient(ellipse 30% 24% at 30% 24%, rgba(255, 214, 202, .40), transparent 70%),
    radial-gradient(circle at 36% 30%, #cf4038 0%, #ab1a1f 48%, #7a0d13 88%, #5d0810 100%);
  box-shadow:
    /* reborde grueso, como el labio que deja la cera al enfriarse */
    inset 0 0 0 clamp(.4rem, 1.6vw, .7rem) rgba(0, 0, 0, .09),
    inset 0 3px 9px rgba(255, 215, 205, .30),
    inset 0 -10px 22px rgba(0, 0, 0, .5),
    0 12px 28px -8px rgba(60, 4, 8, .62);
  transition: transform .55s var(--e-suave), opacity .55s var(--e-suave);
}
.sobre__sello::before {
  /* Anillo grabado en la cera. */
  content: "";
  position: absolute; inset: 8%;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, .24);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, .18);
}

/* ── Texto de la invitación grabado en la cera ─────────────────── */
.sobre__texto {
  position: relative;
  display: grid;
  gap: .45rem;
  justify-items: center;
  text-align: center;
  /* Ancho explícito por dos razones: sin él, el `justify-items: center` del
     sello dimensiona esto a fit-content, colapsa al ancho de la palabra más
     larga y parte el nombre en tres líneas. Y va aquí en % (contra el
     content-box del sello) en lugar de padding en el sello, porque un
     padding porcentual sobre un elemento `position: absolute` se resuelve
     contra el bloque contenedor —la pantalla completa— no contra el sello. */
  width: 74%;
  min-width: 0;
  /* Crema cálida sobre cera: el relieve lo dan las dos sombras. */
  color: #f9e6d9;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .5), 0 -1px 0 rgba(255, 190, 175, .22);
}
.sobre__texto .portada__eyebrow {
  font-size: clamp(.52rem, 1.9vw, .64rem);
  letter-spacing: .3em;
  opacity: .82;
}
.sobre__texto .portada__nombre {
  font-size: clamp(1.6rem, 7vw, 2.5rem);
  line-height: 1.02;
}
.sobre__texto .portada__linea {
  width: 2.6rem;
  background: currentColor;
  opacity: .5;
  margin-block: .15rem;
}
.sobre__texto .portada__sub {
  font-size: clamp(.56rem, 2.1vw, .7rem);
  letter-spacing: .2em;
  opacity: .88;
}

/* El hint vive fuera del sello, sobre el papel: así la cera no se
   satura y la indicación se sigue leyendo. */
.portada--sobre .portada__hint {
  position: absolute;
  left: 50%; bottom: 11%;
  translate: -50% 0;
  z-index: 3;
  margin: 0;
  /* Una sola línea: partida en dos se lee como texto, no como indicación. */
  white-space: nowrap;
  letter-spacing: .18em;
  color: #3f2c14;
}

/* `translate` centra el sello; el transform sólo escala y gira, si no se
   sumaría un segundo -50% y el sello saltaría al abrir. */
.portada--sobre.es-abierta .sobre__sello {
  transform: scale(1.22) rotate(-7deg);
  opacity: 0;
}

/* ── 3. Puertas: dos hojas que se van a las orillas con un flash ─ */
.portada--puertas {
  --portada-retraso: 1s;
  padding: 0;
  overflow: hidden;
  background: #0b0a12;
}
.puerta__hoja {
  position: absolute; top: 0; bottom: 0;
  width: 50.6%;
  background:
    linear-gradient(180deg, rgba(201,162,255,.10), transparent 45%),
    linear-gradient(150deg, #191825 0%, #2f2450 52%, #4b2c7d 100%);
  transition: transform 1.15s cubic-bezier(.76, 0, .24, 1);
}
.puerta__hoja--izq { left: 0;  box-shadow: inset -1px 0 0 rgba(201,162,255,.5); }
.puerta__hoja--der { right: 0; box-shadow: inset  1px 0 0 rgba(201,162,255,.5); }
.portada--puertas.es-abierta .puerta__hoja--izq { transform: translateX(-101%); }
.portada--puertas.es-abierta .puerta__hoja--der { transform: translateX(101%); }

.portada--puertas .portada__caja {
  position: relative;
  z-index: 2;
  transition: opacity .4s var(--e-suave);
}
.portada--puertas.es-abierta .portada__caja { opacity: 0; }

.destello {
  position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  z-index: 3;
  width: 3px; height: 100%;
  background: #fff;
  box-shadow: 0 0 40px 14px rgba(255, 255, 255, .85),
              0 0 120px 60px rgba(201, 162, 255, .55);
  opacity: 0;
  pointer-events: none;
}
.portada--puertas.es-abierta .destello { animation: destellar 1.1s cubic-bezier(.2, .75, .2, 1) forwards; }
@keyframes destellar {
  0%   { opacity: 0;   transform: scaleX(1); }
  10%  { opacity: 1;   transform: scaleX(1.6); }
  38%  { opacity: .95; transform: scaleX(46); }
  100% { opacity: 0;   transform: scaleX(240); }
}

@media (prefers-reduced-motion: reduce) {
  /* Sin animación el retraso sólo dejaría la portada colgada en pantalla. */
  .portada { transition-delay: 0s !important; }
  .destello { display: none; }
}

/* ── Encabezado del evento ────────────────────────────────────── */
.inv-hero {
  min-height: 100svh;
  display: grid; place-items: center;
  text-align: center;
  padding: clamp(4rem, 12vh, 8rem) 0;
  position: relative;
  background: var(--hero-fondo, transparent);
  color: var(--hero-texto, inherit);
  overflow: hidden;
}
.inv-hero__brillo {
  position: absolute; inset: 0;
  background: var(--hero-brillo, none);
  pointer-events: none;
}
.inv-hero__caja { position: relative; display: grid; gap: 1.1rem; justify-items: center; }
.inv-hero__eyebrow { font-size: var(--t-xs); letter-spacing: .35em; text-transform: uppercase; color: var(--inv-acento); }
.inv-hero__nombre {
  font-family: var(--inv-display);
  font-size: clamp(3rem, 1.5rem + 9vw, 6rem);
  line-height: .95;
}
.inv-hero__nombre em { font-style: italic; }
.inv-hero__frase { max-width: 32ch; color: var(--inv-suave); font-size: var(--t-lg); }
.inv-hero__meta {
  margin-top: .8rem;
  display: flex; flex-wrap: wrap; gap: .6rem 2rem; justify-content: center;
  font-size: var(--t-sm); letter-spacing: .16em; text-transform: uppercase;
}
.inv-ornamento { display: grid; place-items: center; gap: .35rem; color: var(--inv-acento); }
.inv-ornamento::before, .inv-ornamento::after {
  content: ""; width: 4.5rem; height: 1px; background: currentColor; opacity: .5;
}
.inv-ornamento span { font-size: .8rem; }

/* ── Foto de fondo difuminada ──────────────────────────────────────
   Se pone `--foto-fondo` en el bloque (una url() o un degradado) y se mete
   un <div class="foto-fondo"> como primer hijo. Sirve igual en el hero y en
   el cierre. El bloque necesita `position: relative` y `overflow: hidden`. */
.foto-fondo {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: var(--foto-fondo);
  background-size: cover;
  background-position: var(--foto-posicion, center);
  filter: blur(var(--foto-blur, 3px)) saturate(var(--foto-saturacion, 1));
  /* blur() difumina también hacia dentro desde los bordes y dejaría un halo
     transparente: la escala empuja ese halo fuera del recorte. Basta con
     algo más del doble del radio de desenfoque; subirla de más sólo recorta
     foto de balde. */
  transform: scale(var(--foto-escala, 1.03));
  pointer-events: none;
}
/* Velo: sin él el texto se pierde sobre las zonas claras de la foto. Es
   radial y no plano —  opaco donde cae el texto, casi transparente en las
   orillas, así la foto se sigue viendo en lugar de quedar lavada. */
.foto-fondo::after {
  content: "";
  position: absolute;
  inset: 0;
  /* El 58 % del centro no es estético, es el mínimo medido: los bloques más
     oscuros de estas fotos son casi negros (RGB ~15) y por debajo de ese
     velo el texto baja de 4.5:1 de contraste. Las orillas no llevan texto,
     así que ahí el velo cae a .30 y la foto se ve casi limpia. */
  background: var(--foto-velo,
    radial-gradient(ellipse 88% 72% at 50% 46%,
                    rgba(253, 252, 249, .66) 0%,
                    rgba(253, 252, 249, .58) 62%,
                    rgba(253, 252, 249, .30) 100%));
}
/* Todo lo que va encima de la foto necesita contexto de apilamiento. */
.inv-hero__caja,
.inv-cierre > .contenedor { position: relative; z-index: 1; }
.inv-cierre { position: relative; overflow: hidden; }

/* ── Bloque de sección de invitación ──────────────────────────── */
.inv-bloque { padding-block: clamp(3.5rem, 9vw, 6rem); }
.inv-bloque--alt { background: var(--inv-fondo-alt); }
.inv-bloque__eyebrow {
  font-size: var(--t-xs); letter-spacing: .3em; text-transform: uppercase;
  color: var(--inv-acento); text-align: center;
}
.inv-bloque__titulo {
  font-family: var(--inv-display);
  font-size: var(--t-2xl);
  text-align: center;
  margin-top: .8rem;
}
.inv-bloque__texto {
  margin-top: 1.2rem;
  text-align: center;
  color: var(--inv-suave);
  max-width: 46ch; margin-inline: auto;
}

/* ── Cuenta regresiva ─────────────────────────────────────────── */
.cuenta {
  margin-top: 2.2rem;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(.4rem, 2vw, 1rem);
  max-width: 30rem; margin-inline: auto;
}
.cuenta__celda {
  padding: clamp(.9rem, 3vw, 1.4rem) .4rem;
  border: 1px solid var(--inv-linea);
  border-radius: var(--r-md);
  text-align: center;
  background: var(--inv-fondo);
}
.cuenta__num {
  display: block;
  font-family: var(--inv-display);
  font-size: clamp(1.7rem, 1rem + 3.6vw, 2.6rem);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--inv-acento);
}
.cuenta__eti {
  display: block; margin-top: .4rem;
  font-size: .58rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--inv-suave);
}
.cuenta__llegado {
  grid-column: 1 / -1;
  font-family: var(--inv-display);
  font-size: var(--t-xl);
  text-align: center;
  color: var(--inv-acento);
}

/* ── Itinerario / detalles ────────────────────────────────────── */
.detalles {
  margin-top: 2.4rem;
  display: grid;
  gap: clamp(1rem, 3vw, 1.6rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.detalle {
  padding: clamp(1.4rem, 3.5vw, 2rem);
  border: 1px solid var(--inv-linea);
  border-radius: var(--inv-radio);
  background: var(--inv-fondo);
  text-align: center;
  display: grid; gap: .45rem;
  align-content: start;
}
.detalle__icono { font-size: 1.35rem; }
.detalle__titulo { font-family: var(--inv-display); font-size: var(--t-lg); }
.detalle__dato { font-size: var(--t-sm); }
.detalle__nota { font-size: var(--t-xs); color: var(--inv-suave); }
.detalle .btn { margin-top: .9rem; justify-self: center; }

/* ── Historia (línea de tiempo) ───────────────────────────────── */
.historia { margin-top: 2.6rem; display: grid; gap: 0; }
.historia__hito {
  position: relative;
  padding: 0 0 2.2rem 2.2rem;
  border-left: 1px solid var(--inv-linea);
}
.historia__hito:last-child { border-left-color: transparent; padding-bottom: 0; }
.historia__hito::before {
  content: "";
  position: absolute; left: -4.5px; top: .5rem;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--inv-acento);
}
.historia__anio { font-size: var(--t-xs); letter-spacing: .2em; text-transform: uppercase; color: var(--inv-acento); }
.historia__titulo { font-family: var(--inv-display); font-size: var(--t-lg); margin-top: .2rem; }
.historia__texto { margin-top: .35rem; color: var(--inv-suave); font-size: var(--t-sm); }

/* ── Galería ──────────────────────────────────────────────────── */
.galeria {
  margin-top: 2.4rem;
  display: grid;
  gap: clamp(.5rem, 1.6vw, .9rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr));
}
.foto {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 0;
  cursor: zoom-in;
  background: var(--foto, var(--inv-fondo-alt));
  display: grid; place-items: end center;
  padding: .8rem;
  transition: transform var(--d-media) var(--e-suave), box-shadow var(--d-media) var(--e-suave);
}
.foto:hover { transform: scale(1.025); box-shadow: var(--sombra-md); }
.foto:nth-child(3n+1) { aspect-ratio: 3 / 4; }
.foto:nth-child(4n+2) { aspect-ratio: 1; }
.foto__eti {
  position: relative;
  font-size: .6rem; letter-spacing: .16em; text-transform: uppercase;
  color: #fff;
  text-shadow: 0 1px 6px rgba(0,0,0,.5);
}
.galeria__nota {
  margin-top: 1rem; text-align: center;
  font-size: var(--t-xs); color: var(--inv-suave);
}

/* ── Carrusel de fotos ────────────────────────────────────────────
   El desplazamiento es scroll-snap nativo: en móvil el swipe funciona sin
   una línea de JavaScript. El JS sólo agrega flechas y puntos. */
.carrusel { position: relative; margin-top: 2.4rem; }

/* De tablet para arriba la galería se sale de la columna de lectura: 46rem
   es el ancho correcto para un párrafo y demasiado angosto para una foto.
   El tope real es la ALTURA —  limitar por ancho dejaba la foto diminuta en
   pantallas grandes. Con proporción 3:2, alto = ancho / 1.5.

   En móvil no se aplica: ahí la columna ya ocupa casi todo y salirse
   desalineaba el carrusel del texto de arriba por unos pocos píxeles. */
@media (min-width: 48rem) {
  .carrusel {
    width: min(92vw, 64rem);
    max-width: calc(80svh * 3 / 2);
    /* Centrado de escape: sale del contenedor sin tocar el marcado. */
    margin-left: 50%;
    translate: -50% 0;
  }
}
.carrusel__pista {
  display: flex;
  gap: clamp(.6rem, 2vw, 1rem);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  border-radius: var(--inv-radio);
  /* La barra de scroll rompe la composición; el gesto y las flechas quedan. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.carrusel__pista::-webkit-scrollbar { display: none; }

.carrusel__foto {
  position: relative;
  flex: 0 0 100%;
  scroll-snap-align: center;
  /* 3:2, la proporción de una foto de cámara: así `cover` no recorta. */
  aspect-ratio: var(--carrusel-proporcion, 3 / 2);
  margin: 0;
  border-radius: var(--inv-radio);
  overflow: hidden;
  background: var(--foto, var(--inv-fondo-alt));
  background-size: cover;
  background-position: center;
}
/* Degradado al pie para que el pie de foto se lea sobre cualquier imagen. */
.carrusel__foto::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 45%;
  background: linear-gradient(to top, rgba(20, 14, 8, .58), transparent);
}
.carrusel__foto figcaption {
  position: absolute;
  inset: auto 0 0;
  z-index: 1;
  padding: 1.1rem 1.3rem;
  color: #fff;
  font-size: var(--t-xs);
  letter-spacing: .18em;
  text-transform: uppercase;
  text-shadow: 0 1px 6px rgba(0, 0, 0, .5);
}

.carrusel__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 2;
  width: 2.9rem; height: 2.9rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, .88);
  color: var(--inv-texto);
  font-size: 1.5rem;
  line-height: 1;
  box-shadow: var(--sombra-sm);
  transition: background-color var(--d-rapida), opacity var(--d-rapida), transform var(--d-rapida) var(--e-suave);
}
.carrusel__nav:hover { background: #fff; transform: translateY(0) scale(1.06); }
.carrusel__nav--prev { left: .7rem; }
.carrusel__nav--next { right: .7rem; }
/* En los extremos la flecha se atenúa en lugar de desaparecer: no salta la
   composición y sigue siendo alcanzable con el tabulador. */
.carrusel__nav[disabled] { opacity: .3; cursor: default; }
.carrusel__nav[disabled]:hover { background: rgba(255,255,255,.88); transform: none; }

.carrusel__puntos {
  display: flex;
  justify-content: center;
  gap: .5rem;
  margin-top: 1.1rem;
}
.carrusel__punto {
  width: 7px; height: 7px;
  padding: 0;
  border-radius: 50%;
  background: var(--inv-linea);
  transition: background-color var(--d-rapida), width var(--d-media) var(--e-suave);
}
.carrusel__punto[aria-current="true"] {
  width: 22px;
  border-radius: 999px;
  background: var(--inv-acento);
}
/* Área táctil alta sin engordar el punto visible. El ensanchado lateral se
   queda en 4 px: con más, las áreas de puntos vecinos se solapan y se toca
   el equivocado (el paso entre puntos es de sólo 15 px). */
.carrusel__punto::before {
  content: "";
  position: absolute;
  inset: -18px -4px;
}
.carrusel__puntos { position: relative; }
.carrusel__punto { position: relative; }

/* Visor a pantalla completa */
.visor {
  position: fixed; inset: 0;
  z-index: 300;
  display: grid; place-items: center;
  padding: clamp(1rem, 5vw, 3rem);
  background: rgba(12,10,9,.94);
  opacity: 0; visibility: hidden;
  transition: opacity var(--d-media) var(--e-suave), visibility var(--d-media);
}
.visor.es-visible { opacity: 1; visibility: visible; }
.visor__foto {
  width: min(100%, 34rem);
  aspect-ratio: 3 / 4;
  border-radius: var(--r-md);
  background: var(--visor-foto, #333);
  display: grid; place-items: end center;
  padding: 1.5rem;
  color: #fff;
  font-size: var(--t-sm); letter-spacing: .16em; text-transform: uppercase;
  text-shadow: 0 1px 8px rgba(0,0,0,.6);
}
.visor__cerrar, .visor__nav {
  position: absolute;
  width: 3rem; height: 3rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255,255,255,.1);
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
}
.visor__cerrar { top: 1rem; right: 1rem; }
.visor__nav--prev { left: 1rem; top: 50%; translate: 0 -50%; }
.visor__nav--next { right: 1rem; top: 50%; translate: 0 -50%; }
.visor__cerrar:hover, .visor__nav:hover { background: rgba(255,255,255,.24); }

/* ── Unas palabras: mensajes de los invitados ─────────────────────
   Se llenan desde /api/mensajes/:evento cuando la invitación tiene
   data-evento-id. Sin base, se queda el contenido que traiga el HTML. */
.palabras {
  margin-top: 2.4rem;
  display: grid;
  gap: clamp(.8rem, 2vw, 1.1rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
}
.palabra {
  position: relative;
  padding: 1.5rem 1.5rem 1.3rem;
  border: 1px solid var(--inv-linea);
  border-radius: var(--inv-radio);
  background: var(--inv-fondo);
  display: grid;
  align-content: start;
  gap: .8rem;
}
/* Comilla de apertura como ornamento, no como texto. */
.palabra::before {
  content: "\201C";
  position: absolute;
  top: .1rem; left: .9rem;
  font-family: var(--inv-display);
  font-size: 3.2rem;
  line-height: 1;
  color: var(--inv-acento);
  opacity: .22;
  pointer-events: none;
}
.palabra__texto {
  position: relative;
  font-family: var(--inv-display);
  font-size: var(--t-lg);
  line-height: 1.4;
  /* Un mensaje larguísimo no debe estirar la tarjeta sin control. */
  overflow-wrap: anywhere;
}
.palabra__firma {
  font-size: var(--t-xs);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--inv-acento);
}
.palabras__vacio {
  grid-column: 1 / -1;
  text-align: center;
  padding: 2.5rem 1rem;
  color: var(--inv-suave);
  font-size: var(--t-sm);
}

/* ── Confirmación (RSVP) ──────────────────────────────────────── */
.rsvp {
  margin-top: 2.4rem;
  max-width: 30rem; margin-inline: auto;
  display: grid; gap: 1rem;
}
.campo { display: grid; gap: .4rem; }
.campo__eti { font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase; color: var(--inv-suave); }
.campo input, .campo select, .campo textarea {
  font: inherit;
  color: inherit;
  padding: .85rem 1rem;
  min-height: 48px;
  border: 1px solid var(--inv-linea);
  border-radius: var(--r-sm);
  background: var(--inv-fondo);
  width: 100%;
}
.campo textarea { min-height: 5.5rem; resize: vertical; }
/* Aviso de que el mensaje puede hacerse público: el invitado tiene que
   saberlo ANTES de escribir, no después. */
.campo__nota { font-size: var(--t-xs); color: var(--inv-suave); line-height: 1.5; }

/* ── Interruptor de permiso ────────────────────────────────────────
   Lo marca el invitado: decide si su mensaje puede salir en la sección
   "Unas palabras...". Es un checkbox de verdad, para que funcione con
   teclado y lectores de pantalla; lo que se dibuja es el ::before. */
.permiso {
  display: flex;
  align-items: flex-start;
  gap: .85rem;
  padding: .95rem 1.1rem;
  min-height: 48px;
  border: 1px solid var(--inv-linea);
  border-radius: var(--r-md);
  background: var(--inv-fondo);
  cursor: pointer;
  transition: border-color var(--d-rapida), background-color var(--d-rapida);
}
.permiso:has(input:checked) { border-color: var(--inv-acento); }
.permiso:has(input:focus-visible) { outline: 2px solid var(--inv-acento); outline-offset: 2px; }

.permiso input { position: absolute; opacity: 0; width: 0; height: 0; }

.permiso__riel {
  position: relative;
  flex: none;
  width: 2.6rem; height: 1.5rem;
  margin-top: .1rem;
  border-radius: 999px;
  background: var(--inv-linea);
  transition: background-color var(--d-media) var(--e-suave);
}
.permiso__riel::before {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 1.125rem; height: 1.125rem;
  border-radius: 50%;
  background: var(--inv-fondo);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
  transition: translate var(--d-media) var(--e-suave);
}
.permiso:has(input:checked) .permiso__riel { background: var(--inv-acento); }
.permiso:has(input:checked) .permiso__riel::before { translate: 1.1rem 0; }

.permiso__texto { display: grid; gap: .2rem; }
.permiso__titulo { font-size: var(--t-sm); }
.permiso__ayuda { font-size: var(--t-xs); color: var(--inv-suave); line-height: 1.5; }
.rsvp__opciones { display: flex; gap: .6rem; flex-wrap: wrap; }
.rsvp__radio {
  flex: 1 1 8rem;
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: .8rem 1rem;
  min-height: 48px;
  border: 1px solid var(--inv-linea);
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  cursor: pointer;
  transition: border-color var(--d-rapida), background-color var(--d-rapida), color var(--d-rapida);
}
.rsvp__radio input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.rsvp__radio:has(input:checked) {
  border-color: var(--inv-acento);
  background: var(--inv-acento);
  color: #fff;
}
.rsvp__radio:has(input:focus-visible) { outline: 2px solid var(--inv-acento); outline-offset: 2px; }
.rsvp .btn { margin-top: .4rem; }
.rsvp__aviso {
  font-size: var(--t-xs); color: var(--inv-suave); text-align: center;
  min-height: 1.2rem;
}
.rsvp__aviso[data-error] { color: #b03636; }

/* Enlace al aviso de privacidad. Va junto al formulario porque la ley pide
   ponerlo a disposición en el momento en que se recaban los datos, no
   escondido en un pie de página. */
.rsvp__legal {
  margin-top: .2rem;
  text-align: center;
  font-size: var(--t-xs);
  color: var(--inv-suave);
  line-height: 1.6;
}
.rsvp__legal a { color: var(--inv-acento); text-decoration: underline; text-underline-offset: 2px; }

/* ── Mesa de regalos + QR ─────────────────────────────────────── */
.regalos { margin-top: 2.2rem; display: grid; gap: .8rem; max-width: 32rem; margin-inline: auto; }
.regalo {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.05rem 1.3rem;
  border: 1px solid var(--inv-linea);
  border-radius: var(--r-md);
  background: var(--inv-fondo);
  font-size: var(--t-sm);
  transition: border-color var(--d-rapida), transform var(--d-rapida) var(--e-suave);
}
.regalo:hover { border-color: var(--inv-acento); transform: translateX(3px); }
.regalo b { font-weight: 400; }
.regalo span { color: var(--inv-suave); font-size: var(--t-xs); }

/* ── Música ───────────────────────────────────────────────────── */
.musica {
  position: fixed;
  top: max(1rem, env(safe-area-inset-top));
  right: 1rem;
  z-index: 90;
  width: 2.9rem; height: 2.9rem;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--inv-linea);
  background: rgba(255,255,255,.82);
  backdrop-filter: blur(10px);
  color: var(--inv-texto);
  font-size: 1.05rem;
  box-shadow: var(--sombra-sm);
  opacity: 0; visibility: hidden;
  transition: opacity var(--d-media), visibility var(--d-media), transform var(--d-rapida) var(--e-suave);
}
.musica.es-visible { opacity: 1; visibility: visible; }
.musica:hover { transform: scale(1.06); }
.musica.suena { color: var(--inv-acento); border-color: var(--inv-acento); }
/* Silenciado: nota tachada. En CSS y no con el carácter combinado ♪̸, que
   Android dibuja descolocado. */
.musica:not(.suena)::after {
  content: "";
  position: absolute;
  width: 1.5rem; height: 1px;
  background: currentColor;
  rotate: -45deg;
  opacity: .8;
}

/* ── Cierre ───────────────────────────────────────────────────── */
.inv-cierre { text-align: center; padding-block: clamp(3.5rem, 9vw, 6rem); }
.inv-cierre__frase {
  font-family: var(--inv-display);
  font-size: var(--t-xl);
  max-width: 26ch;
  margin-inline: auto;
  line-height: 1.3;
}
.inv-cierre__firma {
  margin-top: 1.6rem;
  font-size: var(--t-xs); letter-spacing: .24em; text-transform: uppercase; color: var(--inv-suave);
}
