/* ============================================================
   The First Night — Sofía & Marco
   Estilos según 01_IMPLEMENTATION_GUIDE.md
   Arquitectura: cada escena = una composición única.
   Ilustración 9:16 al centro, textos superpuestos en Safe Areas.
   Papel canvas ocupando el resto del viewport.
   ============================================================ */

/* ---------- Fuentes locales (guide sección 4) ---------- */
@font-face {
  font-family: "Cormorant Garamond";
  src: url("fonts/CormorantGaramond-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Manrope";
  src: url("fonts/Manrope-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Manrope";
  src: url("fonts/Manrope-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  /* Paleta cerrada — guide sección 5 */
  --canvas: #F6F2EA;
  --arena: #DCCBB6;
  --lino: #EFE8DD;
  --azul-egeo: #405A78;
  --bruma-marina: #AEBFCC;
  --oro-calido: #D8B87A;
  --verde-olivo: #7A8264;
  --carbon-calido: #3E3830;

  /* Espaciado base 8px */
  --sp-1: 8px;
  --sp-2: 16px;
  --sp-3: 24px;
  --sp-4: 32px;
  --sp-6: 48px;
  --sp-8: 64px;
  --sp-12: 96px;
  --sp-16: 128px;

  /* Márgenes por dispositivo */
  --margin: 32px;

  /* Tipografía */
  --f-editorial: "Cormorant Garamond", "EB Garamond", Georgia, serif;
  --f-ui: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  font-family: var(--f-ui);
  font-size: 16px;
  color: var(--carbon-calido);
  background: var(--canvas);
  min-height: 100vh;
  overflow-x: hidden;
}
img, video { max-width: 100%; height: auto; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }
h1, h2, h3, h4, p, ol, ul, dl, dd, dt, figure { margin: 0; padding: 0; }
ol, ul { list-style: none; }
a { color: inherit; text-decoration: none; }
main { display: block; }

/* ============================================================
   SCENE COLOR BRIDGE SYSTEM
   -----------------------------------------------------------------
   Tokens atmosféricos derivados de los frames reales de cada
   vídeo — muestreados con Python + imageio + numpy sobre bandas
   superior/inferior filtrando personas, arquitectura oscura y
   objetos muy saturados. No inventados, no de paleta de marca.
   ============================================================ */
:root {
  /* Todos los tokens derivan del muestreo directo de los frames
     reales de los MP4 (imageio + numpy). Los "body" son el color
     central del vídeo — el que domina cuando la escena está en
     su parte estable, sin bordes de máscara — y los "in/out"
     son los últimos píxeles reales de los bordes superior/inferior
     que conectan con la escena vecina. */

  --atm-novios-body:     #F6C491;
  --atm-novios-exit:     #EAC7A9;

  --atm-ceremonia-in:    #DDBFA6;
  --atm-ceremonia-body:  #CE9C7B;
  --atm-ceremonia-out:   #E7C4A6;

  /* Dresscode: el "blanco cálido real" del vídeo (marfil #ECD6BD)
     es el body de la escena. La banda superior tiene un tinte
     lavanda ligero (#DABDC7) que aparece solo en los primeros
     píxeles y no se propaga al cuerpo. */
  --atm-dresscode-in:    #ECD6BD;
  --atm-dresscode-body:  #ECD6BD;
  --atm-dresscode-out:   #F5EBDD;

  --atm-banquete-in:     #F2D9C3;
  --atm-banquete-body:   #A38A68;
  --atm-banquete-out:    #D4AC89;

  /* Fiesta termina en crema luminoso, no en marrón — corregido
     tras el feedback de Daniela. */
  --atm-fiesta-in:       #B8845D;
  --atm-fiesta-body:     #A47D60;
  --atm-fiesta-out:      #F3D9BC;

  /* Bases de las escenas HTML editoriales — el cuerpo real que
     ocupa el 85-90% de la altura de cada escena. */
  --atm-complementary-base:  #E8CFB5;
  --atm-protocolo-base:      #E6D5BE;
  --atm-timeline-base:       #EAD8BE;

  /* RSVP: papel canvas cálido neutro. */
  --atm-rsvp-body:           #F1E7D5;
}

/* ---------- Layer 01a · Fallback plano bajo todo -------------
   Cuando el navegador no tenga tiempo de pintar el fondo de una
   escena, se ve este color hueso continuo — evita flashes de
   color inesperado durante la carga. */
.canvas {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--canvas);
  pointer-events: none;
}

/* ============================================================
   ESCENA — composición única
   ============================================================ */

/* La escena ocupa el alto del viewport y centra su stage 9:16.
   Cada escena tiene un fondo cromático propio definido en la sección
   "SCENE COLOR BRIDGES". Sin textura de papel intermedia: el blending
   entre escenas es puramente cromático, dos colores por unión (exit de
   la escena anterior → entry de la escena siguiente), sin colores
   intermedios manuales ni imagen raster interpuesta. */
.scene {
  position: relative;
  width: 100%;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: 0;
  overflow: hidden;
  isolation: isolate;
}

/* Stage — en móvil llena TODO el viewport de su .scene (sin aspect-ratio
   forzado) para que el vídeo con object-fit: cover encaje pixel-perfect
   contra la sección siguiente. En desktop recupera el ratio 9:16 y se
   centra con canvas a los lados.
   Sin background propio: la textura del .canvas overlay se ve entre
   frames del vídeo o mientras carga; así no hay salto de color entre
   el stage y las secciones adyacentes. */
.scene__stage {
  position: relative;
  width: 100%;
  height: 100svh;
  max-width: 100vw;
  overflow: hidden;
  background: transparent;
}
@media (min-width: 768px) {
  .scene__stage {
    aspect-ratio: 9 / 16;
    height: 100svh;
    max-height: 900px;
    width: auto;
    max-width: none;
  }
}

/* Layer 02 · Luma / ilustración full bleed */
.scene__stage > video,
.scene__stage > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 1;
}

/* Layer 03 · HTML absoluto dentro del stage */
.scene__content {
  position: absolute;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: 8% 8%;
  text-align: center;
  align-items: center;
  pointer-events: none;
}
.scene__content > * { pointer-events: auto; }

/* Posiciones — para elegir Safe Area por escena.
   Empezamos todas con --top/--bottom; ajustaremos escena a escena. */
.scene__content--top {
  top: 0;
  left: 0;
  right: 0;
}
/* Baja el bloque top ~20% de la altura del stage — usado en Scene 01
   para dejar respirar la ilustración por encima de los nombres. */
.scene__content--top.scene__content--lowered {
  top: 10%;
}
/* Baja el bloque top al 40% de la altura del stage — usado en Scene 03
   Ceremony para que el título "La Ceremonia" respire por encima de la
   ilustración. */
.scene__content--top.scene__content--top-40 {
  top: 40%;
}
/* Baja el bloque top al 30% de la altura del stage — usado en Scene 06
   Banquete para posicionar 'El Banquete' + info sobre el cielo/mar. */
.scene__content--top.scene__content--top-30 {
  top: 30%;
}

/* Banquete: bloque informativo como capa superpuesta independiente
   sobre la escena, en la zona libre de cielo a la izquierda de la
   luna. .scene__stage ya es position: relative; este bloque se
   ancla dentro con coordenadas absolutas y transform translate
   -50% -50% para que left/top representen el centro del bloque. */
/* Bloque tipográfico del Banquete (escena atmosférica).
   Contenedor absoluto anclado al 18vw izquierdo (+5pp desde 13vw),
   centrado verticalmente en el 47% de la escena. Contenido centrado
   horizontalmente (align-items + text-align: center).
   isolation:isolate crea el stacking context necesario para que el
   ::before (piedra decorativa) quede DETRÁS del texto con z-index:-1
   sin escapar detrás del fondo del scene. */
.banquete-block {
  position: absolute;
  left: 18vw;
  top: 36%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  text-align: center;
  max-width: 60vw;
  pointer-events: none;
  isolation: isolate;
}
.banquete-block > * { pointer-events: auto; }

/* ============================================================
   BANQUETE ↔ MENÚ (intercambio de vistas dentro de #scene-06).
   La copa, la textura de piedra y la copa-shine NO se tocan —
   solo el bloque de texto conmuta entre la vista Banquete y la
   vista Menú por opacity + visibility.

   IMPORTANTE (fix Safari iOS): la vista oculta usa
   visibility:hidden con delay igual a la duración del fade-out.
   Sin visibility, los descendientes de la vista oculta seguían
   interceptando el tap táctil (elementFromPoint devolvía un <p>
   detrás del botón MENÚ). pointer-events:none en el padre NO se
   hereda a los descendientes, por eso la solución robusta es
   visibility (que sí bloquea todo el árbol).
   ============================================================ */
#scene-06[data-view="banquete"] .banquete-view {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 700ms ease 500ms, visibility 0s;
}
#scene-06[data-view="banquete"] .banquete-menu-view {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 500ms ease, visibility 0s 500ms;
}
#scene-06[data-view="menu"] .banquete-view {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 500ms ease, visibility 0s 500ms;
}
#scene-06[data-view="menu"] .banquete-menu-view {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 700ms ease 500ms, visibility 0s;
}

/* Velas se desvanecen (fade out) cuando entramos al menú y
   reaparecen (fade in) al volver a la vista de banquete —
   mismo timing que las vistas de texto para leerse como una
   transición coordinada.

   !important necesario: .banquet-candles tiene la clase .reveal
   y GSAP fromTo aplica `opacity: 1` inline al entrar la escena
   en viewport. Inline style bate a CSS normal por especificidad.
   Estas reglas de toggle vista DEBEN ganar sobre el estado del
   reveal system, así que forzamos con !important. */
#scene-06[data-view="banquete"] .banquet-candles {
  opacity: 1 !important;
  transition: opacity 700ms ease 500ms;
}
#scene-06[data-view="menu"] .banquet-candles {
  opacity: 0 !important;
  transition: opacity 500ms ease;
}

/* CTA editorial dentro de Banquete (mismo lenguaje que el
   .details__control: sin cápsula, sin fondo, letter-spacing amplio,
   subrayado sutil que se expande al hover). */
.banquete-cta {
  background: transparent;
  border: 0;
  /* Padding vertical amplio → hit-target ≥44x44px sin cambiar la
     tipografía visible (spec Apple Human Interface Guidelines). */
  padding: 12px 24px;
  margin-top: var(--sp-3);
  cursor: pointer;
  font-family: var(--f-editorial);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--carbon-calido);
  position: relative;
  /* z-index alto — por encima de textura piedra (0), copa (2) y
     stacking del bloque (3). El botón queda siempre en el top. */
  z-index: 20;
  white-space: nowrap;
  transition: color 320ms ease, letter-spacing 320ms ease;
  /* Habilita explícitamente el touch en iOS Safari; manipulation
     elimina el 300ms delay y desactiva el double-tap zoom sobre
     el botón. */
  pointer-events: auto;
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  /* Reset default Safari button styles */
  -webkit-appearance: none;
  min-height: 44px;
  min-width: 44px;
}
.banquete-cta::after {
  content: "";
  position: absolute;
  left: 20%;
  right: 20%;
  bottom: -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0.5);
  transform-origin: center;
  transition: transform 320ms ease, opacity 320ms ease;
  opacity: 0.5;
}
.banquete-cta:hover,
.banquete-cta:focus-visible {
  color: var(--azul-egeo);
  letter-spacing: 0.30em;
  outline: none;
}
.banquete-cta:hover::after,
.banquete-cta:focus-visible::after {
  transform: scaleX(1);
  opacity: 0.85;
}

/* Vista del Menú: mismo anclaje que .banquete-block, alto suficiente
   para su contenido. Contenido tipográfico centrado en columna. */
.banquete-menu-view {
  position: absolute;
  left: 18vw;
  top: 47%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 66vw;
  gap: var(--sp-2);
  pointer-events: none;
  isolation: isolate;
}
.banquete-menu-view > * { pointer-events: auto; }
.banquete-menu__title {
  margin: 0 0 var(--sp-3) 0;
  line-height: 1;
}
.banquete-menu__course {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  margin: 0;
}
.banquete-menu__course .editorial-h3 {
  color: var(--azul-egeo);
  font-size: 13px;
  letter-spacing: 0.28em;
  margin: 0;
}
.banquete-menu__course .body {
  font-size: 13px;
  line-height: 1.5;
  max-width: 30ch;
  margin: 0;
}
.banquete-menu__ornament {
  display: block;
  width: 46px;
  height: auto;
  opacity: 0.85;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
  margin: 4px 0;
}
.banquete-menu-view .banquete-cta--back {
  margin-top: var(--sp-3);
}
@media (min-width: 768px) {
  .banquete-menu__course .body { font-size: 15px; }
  .banquete-menu__course .editorial-h3 { font-size: 14px; }
  .banquete-menu__ornament { width: 55px; }
}
/* Textura arquitectónica de piedra mediterránea.
   No es placa/tarjeta/marco: es una capa atmosférica más grande que
   el bloque de texto, que se extiende hacia arriba/abajo/laterales
   más allá de él. Los cuatro bordes desaparecen progresivamente en
   el crema-melocotón mediante mask radial → no se ve la forma
   rectangular del PNG.
   Escala vertical ≈ 44% (aprox mitad de la presencia visible de la
   copa, que ocupa 82%). Ancho amplio (78vw) para que se lea como
   sección de muro, no como panel local. */
.banquet-composition::before {
  content: "";
  position: absolute;
  top: 38%;
  left: 30vw;
  transform: translate(-50%, -50%);
  width: 92vw;
  height: 44%;
  background: url("media/banquete-piedra.png") center / cover no-repeat;
  opacity: 0.42;
  mix-blend-mode: multiply;
  z-index: 0;
  pointer-events: none;
  /* Máscara elíptica ampliada — zona central sólida más grande y
     transición más gradual para que se perciba la textura, juntas
     e irregularidades de la piedra. Los cuatro bordes siguen
     desapareciendo en transparent sin dejar rectángulo. */
  -webkit-mask-image: radial-gradient(
    ellipse 62% 58% at center,
    rgba(0, 0, 0, 1) 25%,
    rgba(0, 0, 0, 0.85) 52%,
    rgba(0, 0, 0, 0.42) 78%,
    rgba(0, 0, 0, 0) 100%
  );
  mask-image: radial-gradient(
    ellipse 62% 58% at center,
    rgba(0, 0, 0, 1) 25%,
    rgba(0, 0, 0, 0.85) 52%,
    rgba(0, 0, 0, 0.42) 78%,
    rgba(0, 0, 0, 0) 100%
  );
}

/* Espaciado vertical interno compacto. Anulamos primero todo margin/
   padding heredado, forzamos gap 0 en cada nivel flex y aplicamos
   line-height explicito para eliminar la altura vertical extra que
   trae la tipografia por defecto. */
.banquete-block,
.banquete-block .info,
.banquete-block .info > div {
  gap: 0;
  row-gap: 0;
}
.banquete-block .info {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.banquete-block .info > div {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.banquete-block h1,
.banquete-block dl,
.banquete-block dt,
.banquete-block dd { margin: 0; padding: 0; }

.banquete-block h1 {
  /* Banquete → HORA: 24px (target 22-28) */
  margin: 0 0 24px 0;
  line-height: 1;
}
.banquete-block dt {
  /* HORA → 20:00 pm y DÓNDE → Torralbenc: 4px (target 3-5) */
  margin: 0 0 4px 0;
  line-height: 1;
}
.banquete-block dd {
  margin: 0;
  line-height: 1.15;
}
/* 20:00 pm → DÓNDE: 16px (target 14-18) */
.banquete-block .info > div:first-child {
  margin-bottom: 16px;
}
/* Torralbenc → separador: 32px (target 28-36) */
.banquete-block .details__ornament {
  margin-top: 32px;
  align-self: center;
}

/* ============================================================
   BANQUETE — INTERCAMBIO DE VISTAS (Banquete ↔ Menú).
   Sustitución visual dentro del MISMO slot narrativo. No acordeón,
   no modal, no nuevo scroll. El contenedor conserva la altura del
   scene__stage (100svh mobile, 9:16 desktop) y las dos vistas se
   superponen absolutas sobre inset:0. Solo la opacity cambia.
   ============================================================ */
/* ============================================================
   BANQUETE · escena editorial-atmosférica.
   Tres capas independientes en un contenedor position:relative con
   overflow:hidden. La escena define la altura (78svh); los
   elementos se ANCLAN a ella, nunca al revés.

   1) .banquet-shadow — sombra proyectada (rama-olivo enmascarada,
      heavy blur, opacity baja, mix-blend-mode multiply). Ninguna
      referencia a una rama ilustrada; solo la mancha difusa.
   2) .banquet-glass-wrap — copa recortada a su bbox real, sobresale
      ~30% del viewport, ocupa ~80% de la altura de la escena.
   3) .banquete-block — texto absoluto a la izquierda (13vw, 47%v).
   ============================================================ */
.banquet-composition {
  position: relative;
  width: 100%;
  height: 100%;
  /* clip horizontal preserva el recorte lateral que necesitan la
     copa (right:-18vw) y la piedra (width:92vw fuera de viewport).
     visible en Y permite que las velas (velas sólidas + sombras +
     brillos) rebosen por el bottom del scene y se solapen sobre
     #detalles sin ser cortadas. */
  overflow-x: clip;
  overflow-y: visible;
  isolation: isolate;
}

/* Sombra proyectada de rama de olivo · SIN aspecto de rama plana.
   - Máscara con rama-olivo.png sobre color oscuro (multiply →
     oscurece el crema del fondo, no lo tapa).
   - filter blur(22px) suaviza mucho la silueta de la rama y hace
     que se lea como sombra difusa, no como decoración botánica.
   - Sale por la esquina superior-izquierda; la mayor parte queda
     fuera del viewport. La densidad se degrada progresivamente
     por el blur pesado + opacity baja + mix-blend-mode: multiply.
   - Un pseudoelemento adicional (::after) rompe la simetría con un
     offset y otro blur — ayuda a que no parezca "una rama". */
/* Elemento puente · estrellas doradas entre Ceremonia y Banquete.
   Franja horizontal absoluta anclada al top de la composition. El
   PNG entra ligeramente sobre-ancho (105vw) para que sus extremos
   queden fuera de pantalla y la banda se sienta orgánica. La
   densidad del asset está en el centro-inferior → transform-origin
   50% 80% para la respiración de escala. */
.banquet-stars {
  position: absolute;
  top: -2%;
  left: 50%;
  width: 105vw;
  aspect-ratio: 1200 / 298;
  margin-left: -52.5vw;
  background-image: url("media/estrellas.png");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  z-index: 1;
  pointer-events: none;
  opacity: 0.72;
  transform-origin: 50% 80%;
  will-change: opacity, transform;
}

/* Copa · geometría anclada al ASPECT REAL del PNG recortado
   (296x900 → aspect-ratio 296/900 = 0.329). El wrap dimensiona la
   copa por altura (80% de la escena) y el ancho sale del aspect —
   así el "ancho visible" corresponde al ancho REAL del cristal, no
   al canvas transparente. */
.banquet-glass-wrap {
  /* Aspect ratio del nuevo asset (312x900 = 0.347). right offset
     calibrado para que ~50% del contenido visible de la copa quede
     fuera del viewport derecho (spec Daniela: 45-55% visible).
     top/height reescalados (x 78/88) al ampliar #scene-06 de 78svh
     a 88svh para hacer sitio a las velas — preservan la geometría
     visual absoluta previa de la copa. */
  position: absolute;
  right: -8vw;
  top: 38%;
  transform: translateY(-50%);
  height: 63%;
  aspect-ratio: 312 / 900;
  z-index: 2;
  pointer-events: none;
  isolation: isolate;
}
.banquet-glass {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: fill;
  user-select: none;
  -webkit-user-drag: none;
  /* drop-shadow sigue el alpha del PNG → añade presencia al tallo
     transparente para que la continuidad bowl→stem→base sea visible
     sobre el fondo crema. */
  filter: drop-shadow(-3px 5px 4px rgba(60, 48, 36, 0.18));
}
/* Destello cálido enmascarado por el alpha de la copa — solo se ve
   luz dentro del cristal, nunca como línea blanca atravesando aire. */
.banquet-glass-shine {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    100deg,
    transparent 42%,
    rgba(255, 232, 200, 0.55) 50%,
    transparent 58%
  );
  -webkit-mask-image: url("media/banquete-copa.png");
  mask-image: url("media/banquete-copa.png");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  mix-blend-mode: screen;
}
@media (min-width: 768px) {
  .banquet-glass-wrap { right: -2vw; height: 63%; }
}
@media (min-width: 1024px) {
  .banquet-glass-wrap { right: 6vw; height: 64%; }
}

/* Composición de velas · cierre narrativo de Banquete.
   - Anclada al bottom del scene-06, entrando por la izquierda.
   - Diagonal visual con la copa: copa arriba-derecha ↔ velas
     abajo-izquierda. No compiten (distintas alturas y lados).
   - z-index 1 → por encima de piedra (0), por debajo de copa (2)
     y del bloque de texto (3). No solapa físicamente ninguno.
   - Sin box-shadow, sin drop-shadow, sin filtros: el halo cálido
     viene baked-in en el PNG.
   - Aspect ratio del asset final tras trim del canvas transparente
     (900×742 = 1.213). El halo y las sombras del PNG se preservan
     con 60px de padding transparente en cada lado.
   - width con clamp() → mínimo 260px, ideal 58vw, máximo 430px.
   - La parte inferior entra ligeramente en la zona de fade a
     #FEFAF2 (68→100% del gradient de #scene-06). Ninguna vela
     ni rama principal invade #detalles: overflow:hidden de
     .banquet-composition recorta cualquier excedente vertical. */
.banquet-candles {
  position: absolute;
  /* Left compensa el 14.3% de canvas transparente izquierdo del
     nuevo asset master (alpha bbox arranca en x=220/1536). Con
     -21.7vw el borde izquierdo del contenido visible queda pegado
     al viewport (x≈0), preservando el registro horizontal previo. */
  left: -21.7vw;
  right: auto;
  /* Anclaje vertical por top calc. 36% = misma referencia que
     .banquete-block. 101px calibrado para que el contenido visible
     de la vela alta arranque en la misma coordenada Y absoluta que
     el asset baseline previo (~y=454 en iPhone 12). */
  top: calc(36% + 101px);
  bottom: auto;
  /* Ancho escalado a 151vw para preservar el ANCHO VISIBLE del
     contenido (~420px en iPhone 12). El nuevo asset master tiene
     solo 71% de canvas ocupado por contenido (vs 92% del baseline
     previo), por eso el container total necesita ser ~29% más
     ancho para mantener la misma huella visual. */
  width: clamp(538px, 151vw, 600px);
  max-width: none;
  height: auto;
  z-index: 1;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  /* Aspect-ratio del canvas master de los 3 assets (1536×1024).
     Las 3 capas comparten este aspect exacto → object-fit contain
     produce escala 1:1 idéntica y offsets 0 en las 3 capas. Cero
     letterbox = registro geométrico perfecto entre capas. */
  aspect-ratio: 1536 / 1024;
}

/* Capas de la composición multicapa. Las tres comparten la MISMA
   geometría del container (inset:0 + object-fit contain + object-
   position center). Cada asset se auto-alinea dentro del container
   según su propio canvas. */
.banquet-candles__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* Sombra — capa inferior. Blur muy ligero (1px) + máscara alpha
   radial que preserva el 65-70% central y disuelve la periferia
   en los 4 lados. Opacity base 0.78. Sin blend-mode: normal
   preserva el tono cálido sin ensuciar #FEFAF2. */
.banquet-candles__shadow {
  z-index: 1;
  opacity: 0.78;
  filter: blur(1px);
  -webkit-mask-image: radial-gradient(
    ellipse 92% 88% at 50% 55%,
    #000 0%,
    #000 55%,
    rgba(0, 0, 0, 0.75) 72%,
    rgba(0, 0, 0, 0.35) 88%,
    transparent 100%
  );
  mask-image: radial-gradient(
    ellipse 92% 88% at 50% 55%,
    #000 0%,
    #000 55%,
    rgba(0, 0, 0, 0.75) 72%,
    rgba(0, 0, 0, 0.35) 88%,
    transparent 100%
  );
}

/* Sólido — velas, cristal, llamas, ramas. Sin ningún filtro,
   máscara, blend ni opacity — 100% nítido, referencia visual.
   Ahora en la capa superior (z:3) para que quede por delante del
   halo luminoso; luz sólo se percibe en la periferia. */
.banquet-candles__solid {
  z-index: 3;
  opacity: 0.8;
}

/* Luz — capa intermedia (detrás del solid). blend screen para que
   los reflejos dorados sumen luminosidad sobre la sombra en la
   periferia sin oscurecer velas/cristales. Opacity 0.70. Máscara
   más amplia y etérea que la sombra (55-65% central + fade más
   suave hasta transparencia total antes de los bordes). */
.banquet-candles__light {
  z-index: 2;
  opacity: 0.70;
  mix-blend-mode: screen;
  /* Blur ligero (1.5px) que suaviza cualquier micro-borde restante
     de la máscara sin destruir los puntos de luz internos. */
  filter: blur(1.5px);
  /* Máscara mucho más extendida: fade continuo desde 25% (sin zona
     sólida rígida) con paradas graduales muy juntas hasta 92%.
     Ninguna transición abrupta → borde imposible de identificar. */
  -webkit-mask-image: radial-gradient(
    ellipse 90% 82% at 50% 68%,
    #000 0%,
    rgba(0, 0, 0, 0.95) 25%,
    rgba(0, 0, 0, 0.78) 45%,
    rgba(0, 0, 0, 0.5) 62%,
    rgba(0, 0, 0, 0.25) 78%,
    rgba(0, 0, 0, 0.08) 90%,
    transparent 100%
  );
  mask-image: radial-gradient(
    ellipse 90% 82% at 50% 68%,
    #000 0%,
    rgba(0, 0, 0, 0.95) 25%,
    rgba(0, 0, 0, 0.78) 45%,
    rgba(0, 0, 0, 0.5) 62%,
    rgba(0, 0, 0, 0.25) 78%,
    rgba(0, 0, 0, 0.08) 90%,
    transparent 100%
  );
}
@media (min-width: 768px) {
  .banquet-candles {
    left: 24px;
    top: auto;
    bottom: 48px;
    width: clamp(340px, 48vw, 520px);
  }
}
@media (min-width: 1024px) {
  .banquet-candles {
    left: 32px;
    top: auto;
    bottom: 56px;
    width: clamp(400px, 38vw, 580px);
  }
}

/* ============================================================
   MENÚ dentro de Detalles · página editorial con ritmo izquierda/
   derecha. Sin cuadrícula rígida, sin cards, sin separadores
   entre categorías (solo un cierre único al final).
   ============================================================ */
.book--menu {
  padding-top: var(--sp-6);
  padding-bottom: var(--sp-6);
  text-align: left;
}
.menu__eyebrow {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--verde-olivo);
  margin: 0 0 var(--sp-8) 0;
  text-align: left;
}
.menu__grid {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  margin: 0;
  padding: 0;
}
.menu__item {
  max-width: 32ch;
  margin: 0;
  padding: 0;
}
.menu__item--left {
  margin-left: 0;
  margin-right: auto;
}
.menu__item--right {
  margin-left: auto;
  margin-right: 0;
  padding-left: 4px;
}
.menu__course {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: clamp(30px, 9vw, 44px);
  line-height: 1;
  color: var(--azul-egeo);
  margin: 0 0 var(--sp-2) 0;
  text-align: left;
}
.menu__desc {
  font-family: var(--f-ui);
  font-weight: 400;
  font-size: 17px;
  line-height: 1.55;
  color: var(--carbon-calido);
  margin: 0;
  text-align: left;
}
/* Ligera variación por bloque para romper el patrón mecánico. */
.book--menu > .menu__grid > .menu__item:nth-child(1) { margin-left: 0; }
.book--menu > .menu__grid > .menu__item:nth-child(2) { margin-left: auto; margin-right: -2%; }
.book--menu > .menu__grid > .menu__item:nth-child(3) { margin-left: 4%; }
.book--menu > .menu__grid > .menu__item:nth-child(4) { margin-left: auto; margin-right: 0; }
.book--menu > .details__ornament {
  display: flex;
  margin: var(--sp-8) auto 0 auto;
}
@media (min-width: 768px) {
  .menu__desc { font-size: 18px; }
}

/* Ceremony · placa editorial. La versión --centered vive en el
   centro exacto del scene__stage detrás de la capa .ceremony-fog.
   Se revela (opacity 0 → 1) tras el tap en la niebla + 2s de delay
   (controlado por initCeremonyFog en main.js).

   Panel semi-transparente con backdrop-filter blur → difumina la
   ilustración solo dentro del área del texto, dando legibilidad
   sin cubrir ni oscurecer la ilustración. Máscara radial disuelve
   los bordes del panel en la ilustración — sin perímetro visible. */
.ceremony-plate {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  padding: 2px;
}
.ceremony-plate .info {
  padding-top: 0;
}

.ceremony-plate--centered {
  position: absolute;
  /* Cielo de la ilustración — parte superior donde no hay
     arquitectura/palmeras/mar. */
  top: 22%;
  left: 50%;
  width: fit-content;
  max-width: 82%;
  /* Padding mínimo — el fondo queda ~2px alrededor del texto */
  padding: 8px 14px;
  transform: translate(-50%, 0);
  z-index: 4;
  text-align: center;
  /* Fondo muy suave con backdrop-blur: difumina la ilustración
     detrás del texto sin cubrirla. Los bordes se disuelven
     totalmente vía la máscara — imperceptibles. */
  background: rgba(246, 216, 190, 0.4);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  -webkit-mask-image: radial-gradient(
    ellipse 95% 95% at 50% 50%,
    #000 0%,
    rgba(0, 0, 0, 0.85) 30%,
    rgba(0, 0, 0, 0.55) 55%,
    rgba(0, 0, 0, 0.22) 80%,
    transparent 100%
  );
  mask-image: radial-gradient(
    ellipse 95% 95% at 50% 50%,
    #000 0%,
    rgba(0, 0, 0, 0.85) 30%,
    rgba(0, 0, 0, 0.55) 55%,
    rgba(0, 0, 0, 0.22) 80%,
    transparent 100%
  );
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms ease;
}
.ceremony-plate--centered.is-revealed {
  opacity: 1;
  pointer-events: auto;
}
@media (min-width: 768px) {
  .ceremony-plate--centered { padding: 12px 20px; }
}

/* ============================================================
   NIEBLA CEREMONIA · capa animada que cubre toda la ilustración
   al entrar a Scene 03. Múltiples radial-gradients simulan una
   nube cálida en movimiento (mezcla de #F6D8BE + blancos +
   dorados suaves). Un tap la disipa (fade + scale + blur) y
   revela la ilustración; 2s después aparece la placa de texto.
   Estado revelado se persiste en sessionStorage (initCeremonyFog).
   ============================================================ */
.ceremony-fog {
  position: absolute;
  inset: 0;
  z-index: 5;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  overflow: hidden;
  /* Múltiples capas con background-size mucho mayor que el
     viewport → el drift produce desplazamiento MÁS EVIDENTE
     (nubes que se mueven perceptiblemente). */
  background:
    radial-gradient(ellipse 90% 70% at 25% 30%, rgba(255, 250, 240, 0.95), rgba(246, 216, 190, 0) 70%),
    radial-gradient(ellipse 85% 65% at 75% 65%, rgba(233, 207, 160, 0.72), rgba(246, 216, 190, 0) 70%),
    radial-gradient(ellipse 75% 55% at 50% 18%, rgba(255, 248, 234, 0.88), rgba(246, 216, 190, 0) 72%),
    radial-gradient(ellipse 80% 60% at 60% 85%, rgba(246, 216, 190, 0.92), rgba(246, 216, 190, 0) 74%),
    #F6D8BE;
  background-size: 180% 160%, 200% 180%, 165% 145%, 175% 155%, auto;
  background-position: 0% 0%, 100% 100%, 30% 0%, 80% 100%, 0 0;
  will-change: opacity, transform, filter, background-position;
  animation:
    ceremony-fog-drift 14s ease-in-out infinite alternate,
    ceremony-fog-breathe 4s ease-in-out infinite;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
/* Drift más amplio (60/40 deltas frente a los 18/82 previos) →
   movimiento perceptible sin ser mareante. */
@keyframes ceremony-fog-drift {
  0%   { background-position: 0% 0%,   100% 100%, 30% 0%,  80% 100%, 0 0; }
  50%  { background-position: 60% 25%, 40% 75%,   70% 35%, 20% 70%,  0 0; }
  100% { background-position: 20% 8%,  85% 92%,   45% 10%, 55% 88%,  0 0; }
}
@keyframes ceremony-fog-breathe {
  0%, 100% { opacity: 0.92; }
  50%      { opacity: 1;    }
}
/* Disolución progresiva de 2s. Blur y scale escalan continuo,
   opacity baja en curva ease-out: la niebla se "abre" desde el
   centro y se disipa hacia los bordes, no un fade plano. */
.ceremony-fog.is-dissolving {
  animation: ceremony-fog-dissolve 2000ms ease-out forwards;
  pointer-events: none;
}
@keyframes ceremony-fog-dissolve {
  0%   { opacity: 1;    filter: blur(0);    transform: scale(1);    }
  25%  { opacity: 0.85; filter: blur(8px);  transform: scale(1.05); }
  55%  { opacity: 0.45; filter: blur(20px); transform: scale(1.15); }
  80%  { opacity: 0.15; filter: blur(36px); transform: scale(1.28); }
  100% { opacity: 0;    filter: blur(50px); transform: scale(1.4);  }
}

/* Corazones enlazados que palpitan sobre la niebla — señal visual
   de tap. Al tap: fade out (los taps van al botón .ceremony-fog
   debajo, por pointer-events:none).
   Selector con `.scene__stage >` para ganar en especificidad al
   `.scene__stage > img` que fuerza inset:0 + 100%×100% + cover. */
.scene__stage > .ceremony-hearts {
  position: absolute;
  /* inset:auto FIRST para resetear el inset:0 heredado del rule
     .scene__stage > img; luego top/left explícitos para centrar. */
  inset: auto;
  top: 50%;
  left: 50%;
  right: auto;
  bottom: auto;
  width: 44vw;
  max-width: 220px;
  height: auto;
  object-fit: contain;
  z-index: 6;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(1);
  animation: ceremony-hearts-beat 1.6s ease-in-out infinite;
  user-select: none;
  -webkit-user-drag: none;
  will-change: transform, opacity;
}
@keyframes ceremony-hearts-beat {
  0%, 100% { transform: translate(-50%, -50%) scale(1);     }
  30%      { transform: translate(-50%, -50%) scale(1.08);  }
  60%      { transform: translate(-50%, -50%) scale(1.02);  }
}
.ceremony-hearts.is-fading {
  opacity: 0;
  transition: opacity 600ms ease;
  animation: none;
}
@media (min-width: 768px) {
  .ceremony-hearts { max-width: 280px; }
}
@media (prefers-reduced-motion: reduce) {
  .ceremony-fog { animation: none; }
  .ceremony-fog.is-dissolving { animation: none; opacity: 0; transition: opacity 400ms ease; }
  .ceremony-hearts { animation: none; }
  .ceremony-plate--centered { transition: opacity 400ms ease; }
}
/* Halo de la placa eliminado — el texto va directo sobre la
   ilustración, sin fondo cremoso detrás. */
.scene__content--bottom {
  bottom: 0;
  left: 0;
  right: 0;
}
.scene__content--left {
  top: 0;
  bottom: 0;
  left: 0;
  width: 60%;
  justify-content: center;
  align-items: flex-start;
  text-align: left;
}
.scene__content--right {
  top: 0;
  bottom: 0;
  right: 0;
  width: 60%;
  justify-content: center;
  align-items: flex-end;
  text-align: right;
}
.scene__content--center {
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
}

/* ============================================================
   PORTAL · experiencia independiente. Se monta al cargar el
   documento y se desmonta cuando el usuario atraviesa la puerta.
   No comparte contenedores con la Historia.
   ============================================================ */
.portal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  background: var(--canvas) url("media/papel-canvas.png") center / cover no-repeat;
  overflow: hidden;
}
.portal__stage {
  position: relative;
  width: 100%;
  height: 100svh;
  aspect-ratio: 9 / 16;
  max-width: 100vw;
  overflow: hidden;
  background: var(--lino);
}
@media (min-width: 768px) {
  .portal__stage {
    height: 100svh;
    max-height: 900px;
    width: auto;
    max-width: none;
  }
}
.portal__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 1;
}
.portal__btn {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: pointer;
  z-index: 2;
  background: transparent;
  border: 0;
}
.portal__btn:focus-visible {
  outline: 2px dashed rgba(76, 106, 138, 0.4);
  outline-offset: -8px;
}

/* ============================================================
   TIPOGRAFÍA (guide sección 4)
   ============================================================ */
.kicker {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--carbon-calido);
}
.hero {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 44px;
  line-height: 1.05;
  color: var(--azul-egeo);
  letter-spacing: 0.005em;
}
.h1 {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 34px;
  line-height: 1.1;
  color: var(--azul-egeo);
}
.h2 {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 24px;
  line-height: 1.2;
  color: var(--carbon-calido);
  font-style: italic;
}
.h3 {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--carbon-calido);
}
.body {
  font-family: var(--f-ui);
  font-weight: 400;
  font-size: 15px;
  line-height: 1.65;
  color: var(--carbon-calido);
  max-width: 34ch;
}
.small {
  font-family: var(--f-ui);
  font-weight: 400;
  font-size: 13px;
  line-height: 1.55;
  color: var(--carbon-calido);
}
.meta {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.12em;
  color: var(--verde-olivo);
  text-transform: uppercase;
}
.body strong { color: var(--azul-egeo); font-weight: 500; }

/* ---------- Info list (Ceremonia, Banquete) ---------- */
.info {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  align-items: center;
  padding-top: var(--sp-2);
}
.info > div {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: center;
}
.info dt {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--verde-olivo);
}
.info dd {
  font-family: var(--f-ui);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.4;
  color: var(--carbon-calido);
}

/* ============================================================
   ESCENAS DE LIBRO (Protocolo, Timeline) — sin ilustración
   Se leen sobre el papel canvas, con carácter editorial.
   ============================================================ */
.scene--book {
  min-height: 100vh;
  padding: var(--sp-12) var(--margin);
  display: block;
}
/* Escenas editoriales cortas (solo texto + puente): no fuerzan
   100vh y viven con márgenes verticales moderados. */
.scene--slim {
  min-height: auto;
  /* Padding vertical con -20% para acercar el bloque a la escena
     siguiente sin comprimir la lectura. */
  padding: calc(var(--sp-6) * 0.8) var(--margin);
}
.book {
  max-width: 640px;
  margin: 0 auto;
  padding: 0;
  text-align: center;
}
.editorial-title {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 56px;
  line-height: 1;
  color: var(--azul-egeo);
  margin-top: var(--sp-2);
  margin-bottom: var(--sp-3);
}
.editorial-intro {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.4;
  font-style: italic;
  color: var(--carbon-calido);
  max-width: 40ch;
  margin: 0 auto;
  padding-bottom: var(--sp-8);
  border-bottom: 1px solid rgba(74, 67, 60, 0.15);
}

/* Bloque complementario a Scene 01 · fecha + intro + elemento puente.
   Sin título ni separadores — respira como una página en blanco de un
   libro. En móvil el bloque de texto se mantiene compacto para que
   respire alrededor del elemento puente. */
.book--complementary {
  padding-top: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.book--complementary .editorial-date {
  /* Elemento clickable (<a>) que abre el .ics del calendario del
     móvil. Estilo editorial: sin subrayado por defecto, appears
     un subrayado fino al hover como affordance. */
  display: inline-block;
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--verde-olivo);
  margin: 0 0 var(--sp-6) 0;
  text-align: center;
  text-decoration: none;
  padding: 4px 8px;
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color 320ms ease, letter-spacing 320ms ease, border-color 320ms ease;
}
.book--complementary .editorial-date:hover,
.book--complementary .editorial-date:focus-visible {
  color: var(--azul-egeo);
  letter-spacing: 0.32em;
  border-bottom-color: currentColor;
  outline: none;
}

/* Enlaces informativos dentro del bloque .info (por ejemplo, la
   ubicación de la ceremonia que abre la app de mapas del móvil).
   Sin subrayado — mantiene el estilo tipográfico del <dd> alrededor,
   solo un fade cromático al hover para señalar la interactividad. */
.info a.info-link {
  color: inherit;
  text-decoration: none;
  transition: color 240ms ease;
  cursor: pointer;
}
.info a.info-link:hover,
.info a.info-link:focus-visible {
  color: var(--azul-egeo);
  outline: none;
}
.book--complementary .editorial-intro {
  border-bottom: 0;
  padding-bottom: 0;
  max-width: 300px;
  font-size: 21px;
  line-height: 1.55;
  text-align: center;
  margin: 0 auto;
}

/* Elementos puente · assets decorativos que conectan escenas.
   Se declara clase base .bridge y modificadores por asset. */
.bridge {
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
  user-select: none;
}
.bridge--waves {
  max-width: 320px;
  /* Margen superior con -20% respecto al anterior. */
  margin: calc(var(--sp-3) * 0.8) auto 0 auto;
}
@media (min-width: 768px) {
  .book--complementary .editorial-intro { max-width: 34ch; font-size: 22px; }
  .bridge--waves { max-width: 420px; margin-top: calc(var(--sp-4) * 0.8); }
}
@media (min-width: 1024px) {
  .bridge--waves { max-width: 520px; }
}

/* ============================================================
   ESCENA DRESS CODE · composición gráfica única.
   El título y los banderines conviven en el MISMO sistema de
   coordenadas absolutas — ninguno empuja al otro. El título se
   superpone al área visual superior del PNG (zona de cielo, sin
   contenido central). No hay flujo vertical, no hay margin/gap
   entre hermanos que provoque el efecto dominó.
   ============================================================ */
.dresscode-composition {
  position: relative;
  width: 100%;
  height: 100%;
}

/* Banderines: absolute, edge-to-edge en el viewport.
   - width: 115vw sobresale ~7.5vw por lado (intencional).
   - left:50% + margin-left:-57.5vw centra sobre viewport
     (margin-left no lo sobreescribe GSAP .reveal como haría el
     transform).
   - bottom:-18.6vw esconde bajo el borde inferior del contenedor el
     ~24% de canvas transparente al pie del PNG. La fila inferior
     visible de los banderines cae exactamente en el borde inferior
     de la escena. */
.dresscode-composition .bridge--banderines {
  position: absolute;
  bottom: -18.6vw;
  left: 50%;
  margin: 0 0 0 -57.5vw;
  width: 115vw;
  max-width: none;
  height: auto;
  display: block;
  opacity: 0.95;
}

/* Título: absolute, superpuesto al área visual superior del PNG.
   El pico central de la cinta (curva superior) está a ~22.4vw del
   borde inferior del contenedor (46.6% del alto del img cuando
   width=115vw). El título se coloca 40px por encima de ese punto
   → la línea base cae ~40px sobre la cinta, en la zona de cielo
   del canvas donde no hay contenido visible del PNG. */
.dresscode-composition .dresscode-bridge__title {
  position: absolute;
  bottom: calc(22.4vw + 40px);
  left: 0;
  right: 0;
  text-align: center;
  margin: 0;
  z-index: 2;
}
.editorial-h3 {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 26px;
  line-height: 1.15;
  color: var(--azul-egeo);
  margin-bottom: var(--sp-1);
}

/* ---------- Protocolo — pausa editorial breve ---------- */
.book__blocks {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  padding-top: var(--sp-6);
  text-align: left;
}
.book__block {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.book__block .body { max-width: 46ch; }

/* ============================================================
   TIMELINE · clímax visual
   Path SVG orgánico que se dibuja con el scroll, nodos que
   aparecen alternando izquierda / derecha, transición del
   trazado hacia azul noche en el último tramo.
   ============================================================ */
.timeline {
  position: relative;
  margin-top: var(--sp-8);
  padding: var(--sp-8) 0 var(--sp-12) 0;
  color: var(--verde-olivo);
  transition: color 1800ms ease;
  isolation: isolate;
}
.timeline--twilight {
  color: var(--azul-egeo);
}
.timeline__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
  opacity: 0.85;
}
.timeline__path {
  stroke-dasharray: 3000;
  stroke-dashoffset: 3000;
}
.timeline__nodes {
  position: relative;
  z-index: 1;
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-16);
}
.timeline__node {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--sp-3);
  align-items: center;
  padding: 0 var(--sp-3);
}
.timeline__icon-wrap {
  width: 88px;
  height: 88px;
  display: grid;
  place-items: center;
  position: relative;
}
.timeline__icon {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 6px 14px rgba(74, 67, 60, 0.08));
}
.timeline__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.timeline__time {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: currentColor;
  margin: 0;
}
.timeline__node h3 { color: currentColor; }
.timeline__body .body { max-width: 40ch; margin-top: 2px; color: var(--carbon-calido); }

/* ============================================================
   CTA
   ============================================================ */
.cta {
  display: inline-block;
  padding: 12px 28px;
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--canvas);
  background: var(--azul-egeo);
  border: 0;
  border-radius: 1px;
  cursor: pointer;
  transition: opacity 180ms ease-out;
  align-self: center;
  margin-top: var(--sp-3);
  text-align: center;
}
.cta:hover, .cta:focus-visible { opacity: 0.85; }
.cta:focus-visible { outline: 2px solid var(--azul-egeo); outline-offset: 3px; }
.cta--submit {
  align-self: stretch;
  margin-top: var(--sp-4);
  padding: 15px 28px;
  background: transparent;
  color: var(--azul-egeo);
  border: 1px solid var(--azul-egeo);
  transition: background 260ms ease-out, color 260ms ease-out, transform 120ms ease-out;
  will-change: transform;
}
.cta--submit:hover, .cta--submit:focus-visible {
  background: var(--azul-egeo);
  color: #FCF6EF;
  opacity: 1;
}

/* ============================================================
   Scene 09 · RSVP invite — farol como botón sobre fondo pintado.
   Al tap el vídeo del farol enciende la vela y todo el bloque
   desaparece en fade → el formulario RSVP aparece debajo.
   ============================================================ */
.scene--rsvp-invite {
  background: #F5E9D3;
}
.rsvp-invite {
  position: relative;
  width: 100%;
  height: 100svh;
  overflow: hidden;
  isolation: isolate;
}
.rsvp-invite__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

.rsvp-invite__header {
  position: absolute;
  top: 12%;
  left: 0; right: 0;
  /* z-index > 10 → queda por encima del .rsvp-seam-glow (z:10) para
     que "¿Nos acompañarás? / Déjanos tu luz." no sea velado por la
     banda cream que cubre el seam. */
  z-index: 12;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 0 24px;
  text-align: center;
  pointer-events: none;
}
.rsvp-invite__title {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 34px;
  line-height: 1.1;
  color: var(--azul-egeo);
  margin: 0;
  /* Por encima de la .rsvp-seam-glow (z:20) para que no quede velado
     por la banda de transición al entrar en scene-09. */
  position: relative;
  z-index: 25;
}
.rsvp-invite__ornament {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  opacity: 0.7;
}
.rsvp-invite__ornament-line {
  display: block;
  width: 26px;
  height: 1px;
  background: var(--verde-olivo);
  opacity: 0.7;
}
.rsvp-invite__ornament-dot {
  display: block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--verde-olivo);
  opacity: 0.75;
}
.rsvp-invite__subtitle {
  font-family: var(--f-editorial);
  font-style: italic;
  font-weight: 400;
  font-size: 20px;
  line-height: 1;
  color: var(--verde-olivo);
  opacity: 0.9;
  margin: 0;
}
.rsvp-invite__deadline {
  position: absolute;
  bottom: 22%;
  left: 0; right: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0 24px;
  text-align: center;
  pointer-events: none;
}
.rsvp-invite__deadline-lead {
  font-family: var(--f-ui);
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--azul-egeo);
  opacity: 0.85;
  margin: 0;
}
.rsvp-invite__deadline-date {
  font-family: var(--f-ui);
  font-size: 15px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--azul-egeo);
  margin: 0;
}
@media (min-width: 768px) {
  .rsvp-invite__title { font-size: 44px; }
  .rsvp-invite__subtitle { font-size: 24px; }
  .rsvp-invite__deadline-lead { font-size: 13px; }
  .rsvp-invite__deadline-date { font-size: 17px; }
}
.rsvp-invite__lantern-btn {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 42%;
  aspect-ratio: 404 / 720;
  /* El poster va como background-image para garantizar el frame
     inicial visible en todos los navegadores/contextos, incluso si
     el <video poster="..."> aún no ha decodificado el primer frame.
     El vídeo se dibuja encima cuando empieza a reproducirse. */
  background: url("media/rsvp/lantern-poster.png") center / contain no-repeat;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  z-index: 2;
  -webkit-tap-highlight-color: transparent;
  transition: transform 200ms ease;
  /* El cuadrado del fondo del vídeo (#FEF3D9, ligeramente más claro
     que el ovalo del guide) queda invisible: darken toma el color
     más oscuro entre botón+vídeo y el fondo pintado, así el rectángulo
     desaparece dejando sólo el farol. */
  mix-blend-mode: darken;
  /* Máscara radial que difumina los bordes del botón — el rectángulo
     del vídeo desaparece suavemente en los laterales para que se
     integre con la sombra difuminada del fondo. */
  -webkit-mask-image: radial-gradient(ellipse 55% 60% at 50% 50%,
                        black 55%, transparent 100%);
          mask-image: radial-gradient(ellipse 55% 60% at 50% 50%,
                        black 55%, transparent 100%);
}
.rsvp-invite__lantern-btn:hover {
  transform: translate(-50%, -50%) scale(1.03);
}
.rsvp-invite__lantern-btn:focus-visible {
  outline: 2px solid var(--azul-egeo);
  outline-offset: 4px;
  border-radius: 4px;
}
.rsvp-invite__lantern-btn[data-firing="true"] {
  pointer-events: none;
  cursor: default;
}
.rsvp-invite__lantern-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  opacity: 1;
  /* El MP4 tiene su fondo ya horneado al color del ovalo de luz
     (#FEF3D9), así el vídeo se funde con la zona de glow del fondo
     sin necesidad de mix-blend-mode ni canal alpha. */
  transition: filter 800ms ease-in;
}
/* Cuando el usuario toca el farol y el vídeo se reproduce, un
   drop-shadow cálido rodea el contorno visible — la llama emite
   luz sobre el papel. Dos capas para un halo más orgánico. */
.rsvp-invite__lantern-btn[data-firing="true"] .rsvp-invite__lantern-video {
  filter:
    drop-shadow(0 0 24px rgba(255, 181, 88, 0.75))
    drop-shadow(0 0 60px rgba(255, 200, 120, 0.45));
}

@media (min-width: 768px) {
  .rsvp-invite__lantern-btn { width: 28%; }
}

/* ============================================================
   SECCIÓN RSVP · independiente, oculta por defecto
   ============================================================ */
.rsvp {
  position: relative;
  padding: var(--sp-12) var(--margin);
  min-height: 100vh;
  /* Fondo pintado (papel + rama de olivo). Full-bleed en cover para
     encajar en cualquier viewport; color-base #F3D9BC de respaldo
     por si la imagen tarda en cargar. */
  background:
    url("media/rsvp/form-background.jpg") center / cover no-repeat,
    var(--atm-fiesta-out);
  isolation: isolate;
  /* Por encima del .rsvp-seam-glow (z:10) — sin este z-index el
     tercio superior del formulario (incluido el greeting) quedaba
     velado por la banda cream de la transición. */
  z-index: 11;
}

.rsvp__inner {
  max-width: 520px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  text-align: center;
  /* z-index > 10 → los textos "CONFIRMAR ASISTENCIA / Confirmar
     asistencia / Cuéntanos si estarás con nosotros…" y el resto
     del formulario quedan por encima del .rsvp-seam-glow (z:10). */
  position: relative;
  z-index: 12;
}
/* Saludo personalizado — bloque de 2 líneas en Cormorant editorial.
   'invitación reservada para' en tamaño pequeño; el nombre del
   invitado más grande. Ambos en la misma familia tipográfica que el
   h1 anterior 'Que estéis ese día' para mantener coherencia editorial.
   Cuando el backend esté vivo, RSVP_CONFIG.guestSlug resuelve el
   nombre y JS lo inyecta en .rsvp__greeting-name. */
.rsvp__greeting {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  /* +10svh de margen superior: baja 'invitación reservada para
     / Familia Ramírez' 10 puntos para que quede claramente
     debajo del área opaca de la .rsvp-seam-glow. */
  margin: 10svh 0 var(--sp-4) 0;
  text-align: center;
}
.rsvp__greeting-lead {
  font-family: var(--f-editorial);
  font-style: italic;
  font-weight: 400;
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--azul-egeo);
  opacity: 0.75;
  line-height: 1.2;
  /* Por encima de la .rsvp-seam-glow (z:20) para que no quede velado
     por la banda cream de la transición entre escenas. */
  position: relative;
  z-index: 25;
}
.rsvp__greeting-name {
  font-family: var(--f-editorial);
  font-style: italic;
  font-weight: 400;
  font-size: 34px;
  line-height: 1.05;
  color: var(--azul-egeo);
}
@media (min-width: 768px) {
  .rsvp__greeting-lead { font-size: 17px; }
  .rsvp__greeting-name { font-size: 40px; }
}

/* Intro del form — mismo estilo que Protocolo desc (Cormorant 14/15,
   carbón cálido). Es la línea 'Sin vosotros no hay mesa. Contadnos
   si podréis venir.' aprobada. */
.rsvp__intro {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.45;
  color: var(--carbon-calido);
  /* margin-top -10svh: sube 10 puntos respecto a su posición
     original (justo debajo del greeting). */
  margin: -10svh 0 var(--sp-6) 0;
  text-align: center;
  max-width: 30ch;
  margin-left: auto;
  margin-right: auto;
}
@media (min-width: 768px) {
  .rsvp__intro { font-size: 17px; }
}

/* Campo select (dropdown) — misma estética editorial que los inputs:
   sin caja, línea inferior. Chevron minimal a la derecha. */
.field--select select {
  padding: 10px 24px 12px 2px;
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 15px;
  color: var(--carbon-calido);
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(62, 56, 48, 0.28);
  border-radius: 0;
  width: 100%;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%234C6A8A' stroke-width='1.4' stroke-linecap='round'><path d='M2 2 L6 6 L10 2'/></svg>");
  background-repeat: no-repeat;
  background-position: right 4px center;
  background-size: 12px 8px;
  transition: border-bottom-color 240ms ease-out;
}
.field--select select:focus-visible {
  outline: none;
  border-bottom-color: var(--azul-egeo);
}
@media (min-width: 768px) {
  .field--select select { font-size: 16px; }
}

/* Mensaje success — bloque que aparece tras enviar. Dos variantes
   (yes / no) alternadas por JS según la selección del usuario. */
.rsvp__success-msg {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-style: italic;
  font-size: 32px;
  line-height: 1.15;
  color: var(--azul-egeo);
  text-align: center;
  margin: 0;
}
@media (min-width: 768px) {
  .rsvp__success-msg { font-size: 40px; }
}

/* Marca de agua Lumii en esquina inferior derecha del form. Sutil,
   opacidad baja; tap → lumii.ch. Firma editorial no comercial. */
.rsvp__lumii-mark {
  position: absolute;
  bottom: 18px;
  right: 18px;
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  color: var(--azul-egeo);
  opacity: 0.55;
  text-decoration: none;
  transition: opacity 240ms ease;
  z-index: 20;
  -webkit-tap-highlight-color: transparent;
}
.rsvp__lumii-mark:hover,
.rsvp__lumii-mark:focus-visible {
  opacity: 0.9;
  outline: none;
}
.rsvp__lumii-mark-line {
  font-family: var(--f-editorial);
  font-style: italic;
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.02em;
}
.rsvp__lumii-mark-name {
  font-family: var(--f-editorial);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.05em;
}
.rsvp__form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  text-align: left;
  margin-top: var(--sp-6);
}
[hidden] { display: none !important; }
.field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  border: 0;
  padding: 0;
  margin: 0;
}
.field label,
.field legend {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--verde-olivo);
  padding: 0;
  margin: 0;
}
.field input[type="text"],
.field input[type="email"],
.field textarea {
  padding: 10px 2px 12px 2px;
  font-family: var(--f-ui);
  font-size: 15px;
  color: var(--carbon-calido);
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(62, 56, 48, 0.28);
  border-radius: 0;
  transition: border-bottom-color 240ms ease-out, border-bottom-width 240ms ease-out;
  width: 100%;
}
.field input:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-bottom-color: var(--azul-egeo);
}
.field textarea { resize: vertical; font-family: var(--f-ui); }
.radio {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  font-family: var(--f-ui);
  font-size: 15px;
  color: var(--carbon-calido);
  padding: 8px 0;
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}
.radio input[type="radio"] {
  accent-color: var(--azul-egeo);
  width: 16px;
  height: 16px;
}
.rsvp__success {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40vh;
  text-align: center;
}
.rsvp__success .hero {
  font-size: 40px;
  color: var(--azul-egeo);
}

/* ============================================================
   REVEAL — estado inicial GSAP
   ============================================================ */
.reveal {
  opacity: 0;
  transform: translateY(12px);
  will-change: opacity, transform;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

/* ---------- Tablet ---------- */
@media (min-width: 768px) {
  :root { --margin: 48px; }
  .kicker { font-size: 12px; }
  .hero { font-size: 56px; }
  .h1 { font-size: 42px; }
  .h2 { font-size: 28px; }
  .h3 { font-size: 15px; }
  .body { font-size: 16px; }
  .info dd { font-size: 15px; }

  .editorial-title { font-size: 64px; }
  .editorial-intro { font-size: 22px; }
  .editorial-h3 { font-size: 30px; }

  .rsvp { padding: var(--sp-16) var(--margin); }
}

/* ---------- Desktop ---------- */
@media (min-width: 1024px) {
  :root { --margin: 64px; }
  .kicker { font-size: 12px; letter-spacing: 0.32em; }
  .hero { font-size: 64px; }
  .h1 { font-size: 48px; }
  .h2 { font-size: 30px; }
  .body { font-size: 17px; }

  .editorial-title { font-size: 72px; }
  .editorial-h3 { font-size: 34px; }

  .rsvp__success .hero { font-size: 56px; }

  /* Protocolo · 2 columnas de bloques con más aire — deja de leerse
     como manual de instrucciones, se siente como una pausa editorial. */
  .book__blocks {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--sp-8);
    row-gap: var(--sp-6);
  }

  /* Timeline · nodos alternando izquierda / derecha con el path
     serpenteando entre ellos. */
  .timeline__nodes { gap: var(--sp-12); }
  .timeline__node {
    grid-template-columns: 128px 1fr;
    gap: var(--sp-4);
    max-width: 44%;
  }
  .timeline__icon-wrap { width: 128px; height: 128px; }
  .timeline__node--left { margin-right: auto; }
  .timeline__node--right {
    margin-left: auto;
    grid-template-columns: 1fr 128px;
    text-align: right;
  }
  .timeline__node--right .timeline__body { order: 1; align-items: flex-end; }
  .timeline__node--right .timeline__icon-wrap { order: 2; }
}

/* ============================================================
   SCENE COLOR BRIDGES — gradientes por escena
   -----------------------------------------------------------------
   Cada escena tiene un fondo cromático propio (color, sin textura;
   la textura del papel viaja por encima con blend multiply). Los
   colores están calibrados a los frames reales de los vídeos
   contiguos para que el borde de un vídeo se disuelva en la
   sección que le sigue sin costura perceptible.
   Las escenas con vídeo llevan además mask-image en el propio
   <video> para desvanecer los bordes superior e inferior — al
   atenuarse, el color de fondo del contenedor (calibrado al
   entry/exit del vídeo) toma el relevo suavemente. No hay overlay
   opaco encima del contenido ilustrado; sólo los últimos píxeles
   del vídeo se disuelven en la atmósfera de la siguiente escena.
   ============================================================ */


/* Scene #detalles · panel editorial. Fondo crema luminoso #FEFAF2
   sustituye al complementary-base beige anterior. Las escenas
   adyacentes (Banquete cierre y Fiesta apertura) también terminan/
   arrancan en #FEFAF2 → costura invisible entre las tres. */
#detalles {
  background: #FEFAF2;
}

/* Estilos de la escena Detalles (compacta, editorial).
   Padding-top y bottom reducidos al 25% del valor previo (150→38,
   165→41 mobile; 180→45, 190→48 desktop) para eliminar el espacio
   vacío estructural entre Banquete → Details → Fiesta. Los gap
   internos entre rama/DETALLES/separador/frase/iconos/labels
   permanecen intactos (definidos en sus propios margins). */
.scene--details {
  min-height: auto;
  padding: 38px 20px 41px 20px;
  position: relative;
  overflow: hidden;
  background: #FEFAF2;
}
@media (min-width: 768px) {
  .scene--details {
    padding: 45px 20px 48px 20px;
  }
}

/* Ramas laterales decorativas — reposicionadas según spec:
   diagonales, cerca del borde lateral, centradas verticalmente
   al 54% del scene. */
.details__side-branch {
  position: absolute;
  top: 54%;
  width: 150px;
  height: auto;
  opacity: 0.85;
  z-index: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
.details__side-branch--left {
  left: -18px;
  transform: translateY(-50%) rotate(-18deg);
}
.details__side-branch--right {
  right: -18px;
  transform: translateY(-50%) scaleX(-1) rotate(-18deg);
}
@media (min-width: 768px) {
  .details__side-branch { width: 180px; }
}

/* Icono vegetal pequeño al inicio del bloque editorial. */
.details__icon-titulo {
  display: block;
  width: 56px;
  height: auto;
  margin: 0 auto 20px auto;
  opacity: 0.85;
  user-select: none;
  -webkit-user-drag: none;
}
@media (min-width: 768px) {
  .details__icon-titulo { width: 70px; }
}

.book--details {
  max-width: 520px;
  margin: 0 auto;
  padding: 0;
  position: relative;
  z-index: 1;
  text-align: center;
}
/* Grid de tres columnas (Protocolo · Timeline · Dress Code) —
   columnas iguales, centrado exacto, ancho compacto según spec.
   Slot uniforme de 72px alto para los iconos; el width del PNG
   varía por icono para igualar peso óptico (sol 72 / paisaje 82
   / percha 62). */
.details__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: end;
  gap: 0;
  margin: 0 auto;
  width: min(340px, 86vw);
  max-width: 340px;
  position: relative;
  z-index: 1;
}
.details__control--icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  position: relative;
  color: var(--azul-egeo);
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color 320ms ease, letter-spacing 320ms ease;
  -webkit-appearance: none;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  touch-action: manipulation;
  min-height: 44px;
}
.details__icon-slot {
  height: 72px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.details__icon-slot img {
  display: block;
  height: auto;
  max-height: 100%;
  object-fit: contain;
  opacity: 0.92;
  user-select: none;
  -webkit-user-drag: none;
}
/* Ancho por icono para equilibrar peso óptico (spec Daniela). */
.details__grid > .details__control--icon:nth-child(1) .details__icon-slot img { width: 72px; }
.details__grid > .details__control--icon:nth-child(2) .details__icon-slot img { width: 82px; }
.details__grid > .details__control--icon:nth-child(3) .details__icon-slot img { width: 62px; }
.details__control--icon > span:last-child { display: block; }

/* Separadores verticales editoriales entre columnas (no en la 1ª).
   Altura fija 74px, 1px, opacity 0.45, centrados verticalmente
   dentro del área del icono → no llegan a los labels. */
.details__grid > .details__control--icon:not(:first-child)::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 1px;
  height: 74px;
  background: var(--verde-olivo);
  opacity: 0.45;
}
.details__control--icon[aria-expanded="true"] {
  letter-spacing: 0.28em;
}
.details__control--icon[aria-expanded="true"] .details__icon-slot img {
  opacity: 1;
}
.details__control--icon:hover,
.details__control--icon:focus-visible {
  color: var(--azul-egeo);
  outline: none;
}
@media (min-width: 768px) {
  .details__grid { max-width: 440px; }
  .details__control--icon { font-size: 14px; letter-spacing: 0.24em; }
  .details__icon-slot { height: 88px; }
  .details__grid > .details__control--icon:nth-child(1) .details__icon-slot img { width: 88px; }
  .details__grid > .details__control--icon:nth-child(2) .details__icon-slot img { width: 100px; }
  .details__grid > .details__control--icon:nth-child(3) .details__icon-slot img { width: 76px; }
  .details__grid > .details__control--icon:not(:first-child)::before { height: 90px; }
}

.details__intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
}
.details__eyebrow {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--verde-olivo);
  margin: 0 0 24px 0;
}
/* Ornamento decorativo · dos líneas finas con un punto central,
   construido íntegramente en CSS (sin carácter textual). */
.details__ornament {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 178px;
  height: 6px;
  margin: 0 0 42px 0;
}
.details__ornament::before,
.details__ornament::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--verde-olivo);
  opacity: 0.45;
}
.details__ornament-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--verde-olivo);
  opacity: 0.6;
}
.details__lead {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-style: italic;
  font-size: 18px;
  line-height: 1.55;
  color: var(--carbon-calido);
  max-width: 330px;
  margin: 0 0 34px 0;
  text-align: center;
}
@media (min-width: 768px) {
  .details__lead { font-size: 20px; max-width: 32ch; }
  .details__ornament { width: 130px; }
}

/* ============================================================
   DRESS CODE · layout editorial mediterráneo.
   Puente ornamental superior + título + separador + descripción
   + 2 filas (icono circular + label + regla horizontal + texto)
   + cierre "Gracias" + corazón. Sin ilustración de fondo.
   ============================================================ */
.book--dresscode {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 24px 40px;
  max-width: 480px;
  margin: 0 auto;
  text-align: center;
}
.dresscode__bridge {
  display: block;
  width: 78px;
  height: auto;
  margin: 0 auto 4px auto;
  opacity: 0.9;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.dresscode__title {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 40px;
  line-height: 1;
  color: var(--azul-egeo);
  margin: 0 0 12px 0;
}
.dresscode__divider {
  margin: 0 0 20px 0;
  width: 60px;
}
.dresscode__lead {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  color: var(--carbon-calido);
  margin: 0 0 34px 0;
}

/* Filas icono + label + descripción */
.dresscode__row {
  display: flex;
  align-items: center;
  gap: 18px;
  width: 100%;
  max-width: 340px;
  margin: 0 auto 22px auto;
  text-align: left;
}
.dresscode__icon {
  flex: 0 0 auto;
  width: 67px;
  height: 67px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.dresscode__icon img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.dresscode__row:first-of-type .dresscode__icon {
  width: 75px;
  height: 75px;
}
.dresscode__row-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}
.dresscode__label {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--azul-egeo);
  margin: 0 0 4px 0;
}
.dresscode__rule {
  display: block;
  width: 32px;
  height: 1px;
  background: var(--verde-olivo);
  opacity: 0.55;
  margin: 0 0 8px 0;
}
.dresscode__desc {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.45;
  color: var(--carbon-calido);
  margin: 0;
}

/* Cierre — ornamento hoja horizontal */
.dresscode__closer {
  display: block;
  width: 180px;
  height: auto;
  margin: 28px auto 6px auto;
  opacity: 0.8;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
@media (min-width: 768px) {
  .book--dresscode { max-width: 540px; }
  .dresscode__title { font-size: 48px; }
  .dresscode__lead { font-size: 18px; }
  .dresscode__icon { width: 78px; height: 78px; }
  .dresscode__row:first-of-type .dresscode__icon { width: 88px; height: 88px; }
  .dresscode__desc { font-size: 15px; }
}

/* ============================================================
   TIMELINE · mismo lenguaje visual que Dress Code.
   Puente ornamental landscape superior + título + separador CSS
   + descripción + 4 filas (icono circular + hora + label +
   descripción) + cierre ornamental + "Gracias" + corazón.
   ============================================================ */
.book--timeline {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 24px 40px;
  max-width: 480px;
  margin: 0 auto;
  text-align: center;
}
.timeline__bridge {
  display: block;
  width: 160px;
  height: auto;
  margin: 0 auto 6px auto;
  opacity: 0.9;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.timeline__title {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 40px;
  line-height: 1;
  color: var(--azul-egeo);
  margin: 0 0 12px 0;
}
.timeline__divider {
  margin: 0 0 20px 0;
  width: 60px;
}
.timeline__lead {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  color: var(--carbon-calido);
  margin: 0 0 30px 0;
}
.timeline__list {
  list-style: none;
  padding: 0;
  margin: 0;
  width: 100%;
  max-width: 340px;
}
.timeline__row {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  margin: 0 0 24px 0;
  text-align: left;
}
.timeline__row:last-child { margin-bottom: 0; }
.timeline__icon-slot {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.timeline__icon-slot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.timeline__row-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  padding-top: 2px;
}
.timeline__time {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 18px;
  line-height: 1;
  color: var(--carbon-calido);
  margin: 0 0 6px 0;
}
.timeline__label {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--azul-egeo);
  margin: 0 0 6px 0;
}
.timeline__desc {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.45;
  color: var(--carbon-calido);
  margin: 0;
}
.timeline__closer {
  display: block;
  width: 180px;
  height: auto;
  margin: 28px auto 6px auto;
  opacity: 0.8;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
@media (min-width: 768px) {
  .book--timeline { max-width: 540px; }
  .timeline__bridge { width: 200px; }
  .timeline__title { font-size: 48px; }
  .timeline__lead { font-size: 18px; }
  .timeline__icon-slot { width: 64px; height: 64px; }
  .timeline__desc { font-size: 15px; }
  .timeline__closer { width: 220px; }
}

/* ============================================================
   PROTOCOLO · mismo lenguaje visual que Dress Code y Timeline.
   Puente ornamental landscape + título + separador CSS +
   descripción + filas (icono circular + label + descripción)
   + "Gracias" cursiva + corazón.
   ============================================================ */
.book--protocol {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 24px 40px;
  max-width: 480px;
  margin: 0 auto;
  text-align: center;
}
.protocol__bridge {
  display: block;
  width: 140px;
  height: auto;
  margin: 0 auto 8px auto;
  opacity: 0.9;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.protocol__title {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 40px;
  line-height: 1;
  color: var(--azul-egeo);
  margin: 0 0 12px 0;
}
.protocol__divider {
  margin: 0 0 20px 0;
  width: 60px;
}
.protocol__lead {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  color: var(--carbon-calido);
  margin: 0 0 30px 0;
}
.protocol__list {
  list-style: none;
  padding: 0;
  margin: 0;
  width: 100%;
  max-width: 340px;
}
.protocol__row {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  margin: 0 0 26px 0;
  text-align: left;
}
.protocol__row:last-child { margin-bottom: 0; }
.protocol__icon-slot {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.protocol__icon-slot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.protocol__row-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  padding-top: 6px;
}
.protocol__label {
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--azul-egeo);
  margin: 0 0 6px 0;
}
.protocol__desc {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.45;
  color: var(--carbon-calido);
  margin: 0;
}
.protocol__closer {
  display: block;
  width: 180px;
  height: auto;
  margin: 28px auto 6px auto;
  opacity: 0.8;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
@media (min-width: 768px) {
  .book--protocol { max-width: 540px; }
  .protocol__bridge { width: 180px; }
  .protocol__title { font-size: 48px; }
  .protocol__lead { font-size: 18px; }
  .protocol__icon-slot { width: 64px; height: 64px; }
  .protocol__desc { font-size: 15px; }
  .protocol__closer { width: 220px; }
}

/* Scenes colapsables (Protocolo, Dresscode, Timeline).
   Cuando están hidden (por defecto) el navegador las oculta con
   display: none. Al abrirlas el JS quita hidden, calcula la altura
   real y anima height + opacity. Fuera del flujo cromático principal:
   fondo plano compartido para percibirlas como una extensión del
   papel que se desplega bajo la escena Detalles. */
/* Hidden UA style (display:none) queda anulado por .scene { display:
   grid }, por lo que forzamos su regreso aquí. Sin esto la scene
   cerrada seguía en el layout con height:0 y opacity:0, pero su
   stacking context podía filtrar el background en el flujo. */
.scene--collapsible[hidden] {
  display: none !important;
}
.scene--collapsible {
  min-height: 0 !important;
  height: 0;
  opacity: 0;
  overflow: hidden;
  padding-top: 0;
  padding-bottom: 0;
}
/* Sobreescribir los backgrounds del sistema de bridges — estas scenes
   ya no forman parte del flujo cromático principal. */
#scene-04.scene--collapsible,
#scene-05.scene--collapsible,
#scene-07.scene--collapsible {
  background: #F2E8D8;
}
.scene--collapsible[aria-expanded="true"] {
  opacity: 1;
}
.scene--collapsible[aria-expanded="true"] {
  padding-top: var(--sp-12);
  padding-bottom: var(--sp-12);
}

/* Regla del sistema: la fusión cromática es local — 6-8% del scene
   por borde como máximo. El cuerpo de cada escena mantiene su
   color/atmósfera natural. */

/* Novios (Scene 01) · el vídeo cubre todo el stage con object-fit:
   cover. El color del contenedor sólo se ve donde la máscara del
   vídeo se desvanece en el borde inferior, y coincide con el exit
   real del vídeo para que no haya salto. */
#scene-01 {
  background: var(--atm-novios-exit);
}
#scene-01 .scene__stage > video {
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 94%, transparent 100%);
  mask-image:         linear-gradient(to bottom, black 0%, black 94%, transparent 100%);
}

/* Complementary (Scene 02) · rediseñada según guía visual.
   Campo cromático central en #F7D3B2 sólido con transiciones
   preservadas hacia novios (top) y ceremonia (bottom). Composición
   atmosférica en capas z-index:
     0 background · 1-2 partículas+destellos+salpicaduras
     3 nubes · 4 pájaros · 5 contenido editorial y separadores. */
#scene-02 {
  /* Campo base LUMINOSO uniforme — sin gradient, sin stops marrones.
     Toda la conexión con las escenas vecinas se resuelve mediante
     capas de OVERLAP atmosférico (::before y ::after) que solapan
     físicamente los boundaries. */
  background: #F7D3B2;
}

.scene--complementary {
  min-height: 65svh;
  height: 65svh;
  padding: 0;
  /* overflow VISIBLE: permite que la capa ::before extienda su
     opacidad por 5% dentro de Scene 01 arriba y 5% dentro de
     Scene 03 abajo. El clipping de assets internos se conserva
     en .complementary__stage. */
  overflow: visible;
  position: relative;
  /* isolation + z-index elevado para que la capa ::before pueda
     renderizar visualmente POR ENCIMA del bg de Novios (arriba) y
     Ceremonia (abajo) en las zonas de solape. */
  isolation: isolate;
  z-index: 2;
}
.complementary__stage {
  position: relative;
  width: 100%;
  height: 65svh;
  min-height: 65svh;
  overflow: hidden;
  isolation: isolate;
  /* z-index elevado → todos los assets internos (clouds, birds,
     text, dividers) renderizan POR ENCIMA de la capa ::before
     (z:0) y de la capa atmosférica (z:1) dentro del stacking de
     scene-02. */
  z-index: 2;
}

/* ============================================================
   CAPA ATMOSFÉRICA ÚNICA — cubre bottom 5% Novios + toda
   Complementaria 02 + top 5% Ceremonia. Color #F6D8BE con
   difuminado alpha en bordes superior/inferior (fade en las
   zonas de solape con las escenas vecinas). Todos los elementos
   de esta sección (textos, imágenes, separadores) renderizan
   POR ENCIMA de esta capa vía .complementary__stage { z-index:1 }.
   ============================================================ */
.scene--complementary::before {
  content: "";
  position: absolute;
  /* -10svh = 10% de Scene Novios. Extiende la capa 10svh dentro
     de Scene 01 arriba. */
  top: -10svh;
  left: 0;
  right: 0;
  /* Altura total = 10svh (solape Novios) + 65svh (Complementaria
     completa tras +30%) + 15svh (solape Ceremonia) = 90svh. */
  height: 90svh;
  pointer-events: none;
  z-index: 0;
  /* Fade alpha: 10svh top (= 11.11% layer) y 15svh bottom
     (= 16.67% layer). Zona 11.11-83.33% opaca al 100% de #F6D8BE. */
  background: linear-gradient(
    to bottom,
    rgba(246, 216, 190, 0)    0%,
    rgba(246, 216, 190, 1)    11.11%,
    rgba(246, 216, 190, 1)    83.33%,
    rgba(246, 216, 190, 0)    100%
  );
}

/* Capa 1-2 · atmósfera combinada: partículas + destellos + estrellas
   + salpicaduras laterales. Asset con contenido en los laterales
   y centro transparente → hace ambas funciones de la spec. Detrás
   de nubes, pájaros y contenido. Opacidad moderada para no competir
   con el texto y quedar percibible detrás de las nubes. */

/* Fecha — top ~7% de la escena, centrada. Preserva tipografía
   y estilo del .editorial-date original (var(--f-ui), 12px,
   0.28em tracking, color olivo). Interactiva (.ics del calendario).
   Centrado horizontal vía inset:left/right + margin auto (no via
   transform, porque el sistema .reveal — GSAP fromTo — sobrescribe
   inline el transform con su propia animación de Y). */
.complementary__date {
  position: absolute;
  top: 10%;
  left: 0;
  right: 0;
  width: fit-content;
  margin: 0 auto;
  z-index: 5;
  /* Redeclaración editorial (antes vivía en .book--complementary
     .editorial-date; al refactorizar la escena fuera del .book,
     replicamos aquí los tokens visuales originales: sans-serif UI,
     mayúsculas, tracking amplio y color olivo). */
  display: inline-block;
  font-family: var(--f-ui);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--verde-olivo);
  text-align: center;
  text-decoration: none;
  padding: 4px 8px;
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color 320ms ease, letter-spacing 320ms ease, border-color 320ms ease;
}
.complementary__date:hover,
.complementary__date:focus-visible {
  color: var(--azul-egeo);
  letter-spacing: 0.32em;
  border-bottom-color: currentColor;
  outline: none;
}

.complementary__divider--top {
  position: absolute;
  top: 18%;
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 30vw;
  max-width: 200px;
  height: auto;
  opacity: 0.8;
  z-index: 5;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

.complementary__intro {
  position: absolute;
  top: 27%;
  left: 0;
  right: 0;
  margin: 0 auto;
  /* Ancho calibrado para producir 5 líneas visuales en mobile con
     la fuente serif itálica actual y line-height 1.62. En iPhone
     12 → 245px produce el wrap exacto de la guía. */
  width: 64vw;
  max-width: 245px;
  padding: 0;
  border: 0;
  text-align: center;
  font-size: 17px;
  line-height: 1.62;
  z-index: 5;
}

.complementary__sprig {
  position: absolute;
  top: 59%;
  left: 0;
  right: 0;
  margin: 0 auto;
  width: 8vw;
  max-width: 52px;
  height: auto;
  z-index: 5;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

/* Nubes asimétricas — masa dominante izquierda, masa secundaria
   derecha-abajo. Registradas para leer como cielo mediterráneo. */
.complementary__cloud {
  position: absolute;
  height: auto;
  z-index: 3;
  opacity: 0.4;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.complementary__cloud--main {
  top: 64%;
  left: -14vw;
  width: 74vw;
  max-width: 460px;
  /* Override del z-index base — la nube izq vive fuera del
     .complementary__stage para poder solapar con Scene 03 sin
     ser clipeada; z:1 la mantiene detrás del contenido del stage
     (birds, text) pero encima del ::before (F6D8BE). */
  z-index: 1;
}
.complementary__cloud--secondary {
  top: 60%;
  left: 48vw;
  width: 62vw;
  max-width: 400px;
}

/* Tres pájaros dispersos — assets individuales para posicionar
   cada uno según spec (centerX 30% / 40% / 70%). No forman grupo
   compacto: dos aves relacionadas a la izquierda + una tercera
   aislada a la derecha. */
/* Wrapper del grupo de pájaros — mantiene las distancias internas
   entre pájaros. Se eliminó el translateY previo: ahora las
   posiciones son explícitas (bird1 arranca en 48 según spec). */
.complementary__birds-group {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}
.complementary__bird {
  position: absolute;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
.complementary__bird--1 {
  top: 74%;
  left: 27vw;
  width: 6.2vw;
  max-width: 44px;
}
.complementary__bird--2 {
  top: 80%;
  left: 37.5vw;
  width: 5.4vw;
  max-width: 36px;
}
.complementary__bird--3 {
  top: 85%;
  left: 66.5vw;
  width: 6vw;
  max-width: 42px;
}

/* Ajustes responsive tablet/desktop — proporciones más contenidas. */
@media (min-width: 768px) {
  .complementary__intro { font-size: 20px; max-width: 420px; width: 60vw; }
  .complementary__divider--top { width: 22vw; max-width: 260px; }
  .complementary__sprig { width: 5vw; max-width: 64px; }
  .complementary__cloud--main { width: 58vw; max-width: 560px; left: -8vw; }
  .complementary__cloud--secondary { width: 48vw; max-width: 500px; left: 46vw; }
  .complementary__bird--1 { width: 4vw; max-width: 52px; }
  .complementary__bird--2 { width: 3.4vw; max-width: 42px; }
  .complementary__bird--3 { width: 3.8vw; max-width: 50px; }
}

/* Ceremonia (Scene 03) · Nuevo orden narrativo Ceremonia → Bridge →
   Banquete. El fondo termina en banquete-in (crema-melocotón claro)
   para que la costura hacia el bridge/Banquete sea continua sin
   introducir una banda intermedia de ceremonia-out (tan más cálido). */
#scene-03 {
  background: linear-gradient(
    to bottom,
    var(--atm-ceremonia-in) 0%,
    var(--atm-banquete-in) 100%
  );
}
#scene-03 .scene__stage > video,
#scene-03 .scene__stage > img {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 6%, black 94%, transparent 100%);
  mask-image:         linear-gradient(to bottom, transparent 0%, black 6%, black 94%, transparent 100%);
}

/* Bridge editorial · Ceremonia → Banquete.
   Escena compacta (min-height auto, sin 100svh) con el separador
   PNG centrado. Fondo cromático empalma con Ceremonia bottom
   (ceremonia-out) arriba y Banquete top (ceremonia-out) abajo →
   sin costura visible entre las tres. */
#scene-cb-bridge {
  min-height: 0;
  height: auto;
  padding: 20px var(--margin);
  display: grid;
  place-items: center;
  overflow: hidden;
  /* Mismo crema-melocotón que cierra Ceremonia y arranca Banquete
     → costura invisible, sin franja intermedia perceptible. */
  background: var(--atm-banquete-in);
}
#scene-cb-bridge > img {
  display: block;
  width: 67.2%;
  max-width: 416px;
  height: auto;
  opacity: 0.8;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
@media (min-width: 768px) {
  #scene-cb-bridge > img { max-width: 512px; }
  #scene-cb-bridge { padding: 28px var(--margin); }
}

/* ============================================================
   CEREMONIA · reflejos del sol sobre el mar.
   Contenedor delimitado por mask-image (rectangular con bordes
   suaves) al área exacta del agua en la ilustración:
   horizontal 25%→78% del stage, vertical 42%→60%. Dentro,
   tres 'glints' independientes con blend screen y animaciones
   desincronizadas (7-11s) simulan el temblor natural del sol
   sobre el Mediterráneo — sin ondas ni displacement.
   ============================================================ */
.ceremony-sea {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* Máscara elíptica única: limita la pintura al área del mar
     (centro en 52% 51%, extensión 30% x 11%). Bordes fade suave
     para que los reflejos no rozen la costa ni el horizonte. */
  -webkit-mask-image: radial-gradient(
    ellipse 30% 11% at 52% 51%,
    rgba(0, 0, 0, 1) 0%,
    rgba(0, 0, 0, 1) 55%,
    rgba(0, 0, 0, 0) 100%
  );
  mask-image: radial-gradient(
    ellipse 30% 11% at 52% 51%,
    rgba(0, 0, 0, 1) 0%,
    rgba(0, 0, 0, 1) 55%,
    rgba(0, 0, 0, 0) 100%
  );
}
/* Micro-segmentos horizontales integrados con los trazos del reflejo
   solar existente en la ilustración. Cada uno es una línea muy fina,
   estrecha en altura, con degradado horizontal cálido y bordes
   suaves. Se colocan mediante custom properties inline (--x/--y/--w)
   generadas por initCeremonySea. */
.ceremony-sea__ripple {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  height: 1.8px;
  transform: translate(-50%, -50%) scaleX(1);
  /* Gradient con centro más denso y cálido (contraste dorado
     ampliado ~35-45% respecto al estado previo). */
  background: linear-gradient(
    to right,
    rgba(255, 205, 130, 0) 0%,
    rgba(255, 205, 130, 0.92) 28%,
    rgba(255, 218, 155, 1) 50%,
    rgba(255, 205, 130, 0.82) 72%,
    rgba(255, 205, 130, 0) 100%
  );
  mix-blend-mode: screen;
  /* Blur mínimo para conservar el borde nítido de la línea de
     reflejo — sin blur excesivo que la disuelva. */
  filter: blur(0.25px);
  border-radius: 50%;
  opacity: 0;
  will-change: opacity, transform;
  pointer-events: none;
}

/* Protocolo bridge (Scene 04) · cuerpo base editorial en 88%. Franjas
   cortas: 6% arriba fusiona con Ceremonia-out, 6% abajo fusiona con
   el body real del vídeo Dresscode (marfil cálido — NO la banda
   lavanda que teñía el pie de Protocolo antes). */
#scene-04 {
  background: linear-gradient(
    to bottom,
    var(--atm-ceremonia-out) 0%,
    var(--atm-protocolo-base) 6%,
    var(--atm-protocolo-base) 94%,
    var(--atm-dresscode-body) 100%
  );
}

/* Dresscode (Scene 05) · cuerpo marfil cálido. Últimos 20% transicionan
   hacia el entry de Banquete para empalmar sin scene HTML intermedia. */
#scene-05 {
  background: linear-gradient(
    to bottom,
    var(--atm-dresscode-body) 0%,
    var(--atm-dresscode-body) 80%,
    var(--atm-banquete-in) 100%
  );
}
#scene-05 .scene__stage > video {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 6%, black 88%, transparent 100%);
  mask-image:         linear-gradient(to bottom, transparent 0%, black 6%, black 88%, transparent 100%);
}

/* Banquete (Scene 06) · Empieza en complementary-base (el mismo color
   con que termina Detalles y cualquier accordion desplegado sobre él),
   fusiona rápido en los primeros 5% al cielo real (banquete-in) y se
   mantiene en el crema-melocotón (banquete-in) hasta el borde inferior.
   El último stop NO usa el token banquete-out (#D4AC89 tan) porque la
   máscara del vídeo desvela ese color en los últimos 6% y crea corte
   con la escena siguiente. Termina en crema #E8CCAE que coincide con
   el inicio de la escena Dress Code, haciendo la costura imperceptible. */
/* Banquete (Scene 06) · escena editorial-atmosférica.
   Altura anclada 78svh (spec Daniela). La composition interna hereda
   height:100% para que la copa y el bloque de texto se referencien a
   una geometría deliberada del contenedor, no al canvas de assets.
   Cromatismo re-calibrado para el nuevo orden narrativo
   (Ceremonia → Banquete → Detalles):
   - Top: banquete-in (mismo crema que cierra Ceremonia y llena el
     bridge → costura invisible entre las tres, sin banda intermedia).
   - Cuerpo: banquete-in.
   - Bottom: complementary-base (empalma con la entrada de Detalles). */
#scene-06 {
  min-height: 0;
  /* Scene 06 reducida un 15% (102svh → 86.7svh; 850px → 723px)
     para acortar el espacio entre Ceremonia y Banquete. Los
     elementos internos (bloque de texto, copa, velas, piedra)
     están posicionados en % → escalan proporcionalmente con la
     reducción sin necesidad de reposicionar. */
  height: min(86.7svh, 723px);
  padding: 0 0 35px 0;
  box-sizing: content-box;
  /* clip horizontal (evita scroll por el offset lateral de la copa)
     + visible vertical (permite que las velas se solapen sobre la
     escena Detalles siguiente). z-index + isolation elevan scene-06
     por encima de #detalles en el paint order → las velas rebosadas
     se renderizan sobre el contenido de Detalles. */
  overflow-x: clip;
  overflow-y: visible;
  position: relative;
  isolation: isolate;
  z-index: 2;
  background: linear-gradient(
    to bottom,
    var(--atm-banquete-in) 0%,
    var(--atm-banquete-in) 54%,
    #FEFAF2 76%,
    #FEFAF2 100%
  );
}
@media (min-width: 768px) {
  #scene-06 { height: 86.7svh; }
}

/* Timeline bridge (Scene 07) · fusión directa Banquete.out → body →
   Fiesta.in. Sin twilight intermedio manual; el gradient final va del
   body cálido claro al color entry del vídeo Fiesta directamente. */
#scene-07 {
  background: linear-gradient(
    to bottom,
    var(--atm-banquete-out) 0%,
    var(--atm-timeline-base) 6%,
    var(--atm-timeline-base) 94%,
    var(--atm-fiesta-in) 100%
  );
}

/* Escena complementaria bridge · Dress Code (entre Banquete y Fiesta).
   Colores muestreados de la niebla real de cada escena adyacente:
   - Top #E8CCAE (crema-melocotón cálido) = mismo color con que cierra
     Scene 06 → costura invisible con Banquete.
   - Bottom #E3C6A5 (crema-melocotón hacia luminoso) = mismo color con
     que abre Scene 08 → costura invisible con Primera Noche.
   Interpolación in oklab: la niebla superior se transforma muy
   gradualmente en la niebla inferior, sin color intermedio inventado.
   Altura acotada explícitamente (clamp) para que la escena se sienta
   como un interludio compacto y no como narrativa completa. Se anula
   el min-height:100svh heredado de .scene. overflow hidden recorta el
   canvas transparente del PNG que se extiende ~18.6vw por debajo del
   borde y el ligero overflow lateral del elemento (115vw). */
#scene-dresscode-bridge {
  background: linear-gradient(
    to bottom in oklab,
    var(--atm-complementary-base) 0%,
    #E3C6A5 100%
  );
  min-height: 0;
  height: clamp(240px, 57.5vw, 560px);
  padding: 0;
  position: relative;
  overflow: hidden;
}

/* Fiesta / La Primera Noche (Scene 08) · el primer stop NO usa
   --atm-fiesta-in (#B8845D marrón profundo) porque la máscara del
   vídeo fade in los primeros 6% desvelan ese color y aparece como
   franja marrón/cobriza bajo Dress Code. Empieza en crema #E3C6A5
   (coincide con el bottom del bridge) y de ahí funde al crema
   luminoso del final del vídeo. */
#scene-08 {
  background: linear-gradient(
    to bottom,
    #FEFAF2 0%,
    var(--atm-fiesta-out) 100%
  );
}
/* Fade top ampliado a 20% (~168px sobre 844) → transición Details
   → Fiesta continua, sin franja horizontal beige entre las escenas.
   El vídeo aparece gradualmente sobre el fondo crema. */
#scene-08 .scene__stage > video {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 20%, black 100%);
  mask-image:         linear-gradient(to bottom, transparent 0%, black 20%, black 100%);
}

/* Scene 09 · RSVP invite (botón). Sin ilustración propia — hereda
   el crema luminoso del final de Fiesta. */
#scene-09 {
  background: var(--atm-fiesta-out);
}

/* ============================================================
   Capa de transición Fiesta → última escena / formulario RSVP.
   Elemento fuera de flujo (height:0) situado en el DOM justo
   después de scene-08 y antes de scene-09. Su ::before es la
   banda visible: se extiende 10svh hacia arriba (dentro del
   inferior de Fiesta) y 10svh hacia abajo (dentro del superior
   de scene-09 O del formulario cuando scene-09 se oculta).
   Color #FCF6EF al ~80% en el centro, bordes 100% difuminados.
   ============================================================ */
.rsvp-seam-glow {
  position: relative;
  width: 100%;
  height: 0;
  pointer-events: none;
  /* z:20 → por encima del .rsvp (z:11) para que la banda siga
     tapando el corte cuando scene-09 desaparece y el formulario
     ocupa su sitio. El tercio central sólido cubre exactamente
     el límite (fiesta bottom / form top); el padding-top del .rsvp
     empuja el greeting suficientemente abajo como para no quedar
     velado. */
  z-index: 20;
}
.rsvp-seam-glow::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  /* 20% del inferior de Fiesta + 20% del superior de la última
     escena / formulario. Banda simétrica de 40svh centrada en el
     seam entre las dos escenas. */
  top: -20svh;
  height: 40svh;
  /* Color #FCEFDE (252, 239, 222). El tercio central es SÓLIDO
     (opacidad 1) — cubre el corte por completo. Los tercios superior
     e inferior se difuminan a transparente para fusionar con Fiesta
     y con la última escena / formulario respectivamente. */
  background: linear-gradient(to bottom,
                rgba(252, 239, 222, 0) 0%,
                rgba(252, 239, 222, 1) 33%,
                rgba(252, 239, 222, 1) 67%,
                rgba(252, 239, 222, 0) 100%);
  pointer-events: none;
}

/* ============================================================
   Landscape-hint · overlay para móvil en landscape.
   Solo visible cuando la media query específica de móvil-landscape
   se cumple. Difumina el resto del contenido y pide girar el
   dispositivo a vertical.
   ============================================================ */
.rotate-hint {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(252, 246, 239, 0.96);
  -webkit-backdrop-filter: blur(24px);
          backdrop-filter: blur(24px);
  place-items: center;
  padding: 24px;
  text-align: center;
  color: var(--azul-egeo);
}
.rotate-hint__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: 320px;
}
.rotate-hint__icon svg {
  width: 82px;
  height: 82px;
  display: block;
  animation: rotate-hint-swing 2.4s ease-in-out infinite;
  transform-origin: center;
}
@keyframes rotate-hint-swing {
  0%, 15%   { transform: rotate(-90deg); }
  55%, 100% { transform: rotate(0deg); }
}
.rotate-hint__title {
  font-family: var(--f-editorial);
  font-weight: 400;
  font-size: 22px;
  line-height: 1.2;
  color: var(--azul-egeo);
  margin: 0;
}
.rotate-hint__subtitle {
  font-family: var(--f-ui);
  font-size: 13px;
  line-height: 1.5;
  color: var(--azul-egeo);
  opacity: 0.7;
  margin: 0;
}

/* Móvil en landscape: solo se dispara cuando la orientación es
   landscape Y la altura es reducida (~teléfono girado). Tablets
   en landscape tienen height >600 y no entran. */
@media (orientation: landscape) and (max-height: 550px) {
  .rotate-hint { display: grid; }
  body > *:not(.rotate-hint) {
    filter: blur(24px);
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
  }
}

/* ============================================================
   Wide-viewport framing · escritorio / iPad / tablet.
   Cuando la ventana es más ancha que un móvil portrait (aspect
   > 9/16), la invitación mantiene su aspecto vertical mobile
   centrada, con fondo blanco estático a los lados — sin rediseño
   ni reposición del contenido interno.
   ============================================================ */
@media (min-aspect-ratio: 9 / 16) and (min-width: 700px) {
  html {
    background: #FFFFFF;
    min-height: 100vh;
  }
  body {
    max-width: min(100vw, calc(100svh * 9 / 16));
    margin: 0 auto;
    background: var(--canvas);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.06);
    overflow-x: hidden;
  }
  /* Elementos con position:fixed inset:0 → constrenerlos a la
     misma columna que el body para que no rebasen a los laterales
     blancos. */
  .portal,
  .canvas,
  .rsvp-seam-glow {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: min(100vw, calc(100svh * 9 / 16));
  }
  /* La .rsvp-seam-glow tiene su propio ::before absoluto — no
     necesita override adicional porque el ::before es hijo del
     div ya constrained. */
}

/* ============================================================
   Affordance papel — elementos interactivos que se comportan
   como tarjetas / trazos de tinta, sin iconos ni pulsos.

   .ink-underline · subrayado que se dibuja de izquierda a derecha
     una única vez cuando el elemento entra en viewport (IO añade
     .is-drawn desde JS). Como si se trazara con tinta.

   .paper-tap · al tap el elemento sube 2px, se libera al soltar.
     Como una tarjeta ligeramente levantada del papel.
   ============================================================ */
.ink-underline {
  position: relative;
  display: inline-block;
}
.ink-underline::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.4s cubic-bezier(0.65, 0.05, 0.36, 1),
              opacity 320ms ease;
  opacity: 0.80;
}
.ink-underline:hover::after,
.ink-underline:focus-visible::after {
  opacity: 1;
}
.ink-underline.is-drawn::after {
  transform: scaleX(1);
}
.paper-tap {
  transition: transform 140ms ease-out;
  will-change: transform;
  -webkit-tap-highlight-color: transparent;
}
.paper-tap:active {
  transform: translateY(-2px);
  transition-duration: 60ms;
}
@media (prefers-reduced-motion: reduce) {
  .ink-underline::after { transition: none; transform: scaleX(1); }
  .paper-tap { transition: none; }
  .paper-tap:active { transform: none; }
}

/* ============================================================
   Language switcher · patrón replicado de lumii.ch/home.html.
   Fixed top-right, muy sutil. Cormorant italic para encajar con el
   lenguaje editorial de la pieza (no sans-serif como la web).
   ============================================================ */
.lang-switcher {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 10000;
  /* Solo visible cuando la Historia está montada — no interfiere con
     el Portal (puerta) que arranca en solitario. Se muestra al pasar
     el body a data-experience='historia'. */
  display: none;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: rgba(252, 246, 239, 0.55);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  border-radius: 999px;
  pointer-events: auto;
}
.lang-switcher button {
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--f-ui);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--azul-egeo);
  opacity: 0.4;
  padding: 2px 4px;
  transition: opacity 240ms ease;
  -webkit-tap-highlight-color: transparent;
}
.lang-switcher button:hover { opacity: 0.7; }
.lang-switcher button.active { opacity: 1; }
.lang-switcher button:focus-visible {
  outline: 1px solid var(--azul-egeo);
  outline-offset: 2px;
}
.lang-switcher span {
  color: var(--azul-egeo);
  opacity: 0.35;
  font-size: 10px;
  font-family: var(--f-ui);
  user-select: none;
}
body[data-experience="historia"] .lang-switcher { display: flex; }
@media (min-width: 768px) {
  .lang-switcher { top: 20px; right: 20px; }
}

/* ============================================================
   Music toggle · botón flotante esquina inferior izquierda.
   Simétrico al .lang-switcher (top-right). Mismo lenguaje visual:
   píldora sutil, backdrop-filter blur, Cormorant italic.
   Estados: .is-playing → opacidad plena + micro-animación de las notas.
   ============================================================ */
.music-toggle {
  position: fixed;
  bottom: 14px;
  left: 14px;
  z-index: 10000;
  width: 40px;
  height: 40px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: rgba(252, 246, 239, 0.55);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  color: var(--azul-egeo);
  opacity: 0.55;
  cursor: pointer;
  transition: opacity 240ms ease, transform 240ms ease;
  -webkit-tap-highlight-color: transparent;
}
.music-toggle svg {
  width: 18px;
  height: 18px;
  display: block;
}
/* Línea diagonal indicando 'apagado' — más clara que solo la opacidad
   para señalar el estado muted. Se desvanece cuando .is-playing. */
.music-toggle::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 1.5px;
  background: currentColor;
  transform: translate(-50%, -50%) rotate(45deg);
  transform-origin: center;
  opacity: 0.9;
  transition: opacity 240ms ease;
  pointer-events: none;
}
.music-toggle.is-playing::after { opacity: 0; }
.music-toggle:hover,
.music-toggle:focus-visible {
  opacity: 0.85;
  outline: none;
}
.music-toggle.is-playing {
  opacity: 1;
}
.music-toggle.is-playing svg {
  animation: music-pulse 2.4s ease-in-out infinite;
}
@keyframes music-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}
@media (min-width: 768px) {
  .music-toggle { bottom: 20px; left: 20px; width: 44px; height: 44px; }
  .music-toggle svg { width: 20px; height: 20px; }
}
@media (prefers-reduced-motion: reduce) {
  .music-toggle.is-playing svg { animation: none; }
}

/* ---------- Prefers Reduced Motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
