.event-page {
  display: block;
  margin: 0;
  min-height: 100vh;
  background: #fff9f4;
}
.xv-experience {
  --event-primary: #6e315f;
  --event-accent: #c99a4a;
  --event-background: #fff9f4;
  --event-text: #2b2028;
  overflow: hidden;
  color: var(--event-text);
  background: var(--event-background);
  font-family: Manrope, system-ui, sans-serif;
  line-height: 1.6;
}
.xv-hero {
  min-height: min(90svh, 52rem);
  padding: clamp(4rem, 12vw, 9rem) 1.5rem;
  display: grid;
  place-content: center;
  text-align: center;
  background:
    radial-gradient(
      circle at 50% 20%,
      color-mix(in srgb, var(--event-accent) 22%, transparent),
      transparent 35%
    ),
    linear-gradient(
      145deg,
      color-mix(in srgb, var(--event-primary) 12%, var(--event-background)),
      var(--event-background)
    );
  background-size: cover;
  background-position: center;
  position: relative;
}
.xv-hero::after {
  content: '';
  position: absolute;
  inset: 1rem;
  border: 1px solid color-mix(in srgb, var(--event-accent) 60%, transparent);
  pointer-events: none;
}
.xv-kicker,
.xv-eyebrow,
.xv-venue-kind {
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--event-primary);
}
.xv-hero h1,
.xv-section h2 {
  font-family: EventoSerif, Georgia, serif;
  font-weight: 500;
  line-height: 0.95;
}
.xv-hero h1 {
  font-size: clamp(4rem, 16vw, 8.5rem);
  margin: 0.25em 0;
  color: var(--event-primary);
  text-wrap: balance;
}
/* Con portada, **todo** el texto del hero va sobre la foto.
   Antes sólo se aclaraban el rótulo, el nombre y el botón: la bienvenida, la
   fecha y la cuenta regresiva conservaban el color oscuro del tema y quedaban
   ilegibles sobre una imagen oscura. Es el defecto que sólo aparece cuando el
   evento tiene portada de verdad, que es siempre en un evento real. */
.xv-hero-cover .xv-kicker,
.xv-hero-cover h1,
.xv-hero-cover .xv-welcome,
.xv-hero-cover time,
.xv-hero-cover .xv-countdown,
.xv-hero-cover .xv-countdown strong,
.xv-hero-cover .xv-calendar {
  color: white;
  border-color: white;
  text-shadow: 0 1px 10px #000;
}
.xv-welcome {
  max-width: 38rem;
  margin-inline: auto;
  font-size: clamp(1rem, 3vw, 1.3rem);
}
.xv-hero time {
  margin-top: 1.5rem;
  font-weight: 800;
}
.xv-countdown {
  margin: 0.75rem 0 0;
}
.xv-countdown strong {
  font-family: EventoSerif, Georgia, serif;
  font-size: 2.25rem;
  color: var(--event-primary);
}
.xv-calendar {
  justify-self: center;
  margin-top: 1.25rem;
  padding: 0.65rem 1rem;
  border: 1px solid var(--event-primary);
  color: var(--event-primary);
  font-weight: 800;
  text-decoration: none;
}
.xv-section {
  max-width: 64rem;
  margin: auto;
  padding: clamp(4rem, 10vw, 7rem) clamp(1.25rem, 5vw, 3rem);
}
.xv-section h2 {
  font-size: clamp(2.5rem, 8vw, 5rem);
  margin: 0.2em 0 0.75em;
  color: var(--event-primary);
  text-wrap: balance;
}
.xv-family {
  text-align: center;
  max-width: 46rem;
}
.xv-ornament {
  color: var(--event-accent);
  font-size: 2rem;
  line-height: 0;
}
.xv-ornament svg {
  display: inline-block;
  vertical-align: middle;
}
.xv-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 1rem;
}
.xv-card {
  border: 1px solid color-mix(in srgb, var(--event-primary) 25%, transparent);
  padding: clamp(1.5rem, 5vw, 2.5rem);
  background: color-mix(in srgb, white 72%, transparent);
}
.xv-card h3 {
  font-family: EventoSerif, Georgia, serif;
  font-size: 2rem;
  margin: 0.2rem 0;
}
/* Todo lo que se pulsa dentro de la invitación mide al menos 44 px de alto.
   Es el tamaño por debajo del cual un pulgar falla, y aquí importa más que en
   ningún otro sitio: casi todo el mundo abre esto desde el teléfono, de pie y
   con una mano. El `inline-flex` da la altura sin separar el texto del párrafo
   en el que vive. */
.xv-card a,
.xv-notes a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  color: var(--event-primary);
  font-weight: 800;
  text-underline-offset: 0.25rem;
}
/* Los enlaces de una tarjeta se apilan en el teléfono: llamar al hotel y abrir
   el mapa son dos acciones distintas y no deben quedar pegadas. */
.xv-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  margin-top: 0.5rem;
}
.xv-copy {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  border: 0;
  padding: 0;
  margin-right: 1rem;
  background: transparent;
  color: var(--event-primary);
  font: inherit;
  font-weight: 800;
  text-decoration: underline;
  text-underline-offset: 0.25rem;
  cursor: pointer;
}
.xv-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 0.75rem;
}
.xv-gallery img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
.xv-itinerary ol {
  list-style: none;
  padding: 0;
}
.xv-itinerary li {
  display: grid;
  grid-template-columns: 4rem 1fr;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid color-mix(in srgb, var(--event-primary) 20%, transparent);
}
.xv-itinerary time {
  color: var(--event-primary);
  font-weight: 800;
}
.xv-notes {
  background: color-mix(in srgb, var(--event-accent) 10%, transparent);
  max-width: none;
  padding-inline: max(calc((100% - 58rem) / 2), 1.25rem);
}
.xv-footer {
  padding: 5rem 1.5rem;
  text-align: center;
  color: white;
  background: var(--event-primary);
}
.xv-footer span {
  font-family: EventoSerif, Georgia, serif;
  font-size: 4rem;
  color: var(--event-accent);
}
.xv-music audio {
  width: min(100%, 32rem);
}
.xv-share {
  display: grid;
  justify-items: center;
  gap: 0.25rem;
  padding: 2rem;
  background: var(--event-background, #fff9f4);
  font-family: Manrope, system-ui, sans-serif;
}
.xv-share button {
  border: 0;
  border-radius: 999px;
  padding: 0.9rem 1.4rem;
  /* La paleta del evento llega en el body; sin ella, el ciruela de siempre. */
  background: var(--event-primary, #6e315f);
  color: white;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 800;
  cursor: pointer;
}
.xv-share .secondary,
.xv-share a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  background: transparent;
  color: var(--event-primary, #6e315f);
  text-decoration: underline;
  text-underline-offset: 0.25rem;
}
.xv-share p {
  min-height: 1.5rem;
}
@media (prefers-reduced-motion: no-preference) {
  .xv-hero > * {
    animation: event-reveal 0.7s both;
  }
  .xv-hero h1 {
    animation-delay: 0.08s;
  }
  .xv-welcome {
    animation-delay: 0.16s;
  }
  @keyframes event-reveal {
    from {
      opacity: 0;
      transform: translateY(1rem);
    }
  }
}
@media (max-width: 520px) {
  .xv-hero {
    min-height: 80svh;
  }
  .xv-section {
    padding-block: 3.5rem;
  }
}

/* —— Cabecera del invitado ———————————————————————————————————————
 *
 * La pone cada pantalla del invitado antes de su tarjeta. Los diseños con
 * cabecera propia traen sus clases; esta es la de reserva y la que da el
 * espaciado a las dos.
 */
.guest-head {
  text-align: center;
  display: grid;
  gap: 0.35rem;
  justify-items: center;
  margin-bottom: 0.5rem;
}
.guest-head-name {
  margin: 0;
  font-family: EventoSerif, Georgia, serif;
  font-size: clamp(1.75rem, 7vw, 2.5rem);
  line-height: 1.1;
  color: var(--event-primary, #59306f);
}
.guest-head-date {
  margin: 0;
  font-size: 0.9rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--event-text, #5c5368) 75%, transparent);
}
/* Aria pinta su nombre y su firma con las clases de su propio theme; aquí sólo
   se ordena el bloque. */
.guest-head-aria {
  gap: 0.75rem;
}

/* —— RSVP y pase ———————————————————————————————————————————————
 *
 * Estas superficies son de la fiesta, no del panel: el color sale de la paleta
 * del evento (`--event-*`) y el violeta queda sólo de respaldo, para cuando no
 * hay evento que mirar (enlace vencido antes de cargar la invitación).
 */

.rsvp-page,
.pass-page {
  background: var(--event-background, #f7f5fb);
  color: var(--event-text, inherit);
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.5rem 0;
}
.rsvp-page main,
.pass-page main {
  width: min(30rem, calc(100% - 2rem));
  display: grid;
  gap: 1rem;
}
.rsvp-head {
  text-align: center;
}
.rsvp-head h1 {
  margin: 0.25rem 0 0.5rem;
  font-size: clamp(1.75rem, 7vw, 2.5rem);
  line-height: 1.1;
}
.rsvp-card {
  background: var(--event-surface, color-mix(in srgb, var(--event-background, #ffffff) 88%, white));
  border: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 22%, transparent);
  border-radius: 1.25rem;
  padding: clamp(1.25rem, 5vw, 2rem);
  display: grid;
  gap: 1rem;
  box-shadow: 0 1rem 3rem #3f245412;
}
/*
 * La apertura: mientras se canjea el enlace (corto o personal) no hay tarjeta,
 * sólo una línea que se dibuja y una etiqueta. Con el enlace personal, un
 * script en la cabeza marca `data-evento-opening` antes de pintar para que el
 * formulario del código no asome; si el canje falla, la marca se retira y
 * aparece la tarjeta con el error.
 */
.evento-opening {
  display: grid;
  justify-items: center;
  gap: 1rem;
  padding: 2rem 0 1rem;
  text-align: center;
}
.evento-opening[data-opening-static] {
  display: none;
}
html[data-evento-opening] .evento-opening[data-opening-static] {
  display: grid;
}
html[data-evento-opening] :is(.rsvp-page, .pass-page) .rsvp-card {
  display: none;
}
.evento-opening-line {
  position: relative;
  width: 5.5rem;
  height: 1px;
  overflow: hidden;
  background: color-mix(in srgb, var(--event-text, #232325) 16%, transparent);
}
.evento-opening-line::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--event-text, #232325);
  transform: scaleX(0.45);
}
.evento-opening p {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--event-text, #232325) 72%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .evento-opening-line::after {
    animation: evento-opening-draw 1.8s cubic-bezier(0.22, 1, 0.36, 1) infinite;
  }
}
@keyframes evento-opening-draw {
  0% {
    transform: scaleX(0);
    transform-origin: left;
  }
  50% {
    transform: scaleX(1);
    transform-origin: left;
  }
  50.01% {
    transform-origin: right;
  }
  100% {
    transform: scaleX(0);
    transform-origin: right;
  }
}
/* El enlace corto en el dominio del producto no sabe de qué fiesta es: papel
   blanco perla en vez del violeta de respaldo. */
.rsvp-page-neutral {
  --event-background: #fafaf8;
  --event-text: #232325;
}

.rsvp-card h1,
.rsvp-card h2 {
  margin: 0;
  font-size: 1.4rem;
}
.rsvp-hint {
  margin: 0;
  color: color-mix(in srgb, var(--event-text, #5c5368) 78%, transparent);
  font-size: 0.95rem;
}
.rsvp-choice {
  border: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.5rem;
}
.rsvp-choice .choice {
  border: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 28%, transparent);
  background: var(--event-surface, color-mix(in srgb, var(--event-background, #ffffff) 40%, white));
  border-radius: 0.75rem;
  padding: 0.85rem 1rem;
  font-weight: 600;
  cursor: pointer;
}
.rsvp-choice .choice-on {
  border-color: var(--event-primary, #59306f);
  /* Contra el papel del evento y no contra blanco: sobre papel oscuro la
     opción elegida salía casi blanca con el texto claro encima. */
  background: color-mix(
    in srgb,
    var(--event-primary, #59306f) 12%,
    var(--event-background, #ffffff)
  );
}
.rsvp-guests select {
  min-height: 2.75rem;
  border-radius: 0.65rem;
  border: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 28%, transparent);
  padding: 0 0.8rem;
  font: inherit;
  width: 100%;
}
.rsvp-note {
  /* El estado se marca con el filete y no con un fondo de color: un verde o un
     rosa pálidos fijos, con el texto claro que hereda una paleta oscura, no se
     leían. La superficie del evento funciona en los dos modos. */
  margin: 0;
  background: var(--event-surface, #eef7f0);
  border-left: 3px solid color-mix(in srgb, #2f7d4f 70%, var(--event-text, #20232d));
  border-radius: 0.4rem;
  padding: 0.7rem 0.9rem;
}
.rsvp-note-error {
  border-left-color: color-mix(in srgb, #e2574c 70%, var(--event-text, #20232d));
}
/* Tarjeta de Clikify tras guardar la respuesta (ADR-074): superficie del
   evento, sin color propio, más callada que la confirmación de arriba. */
.rsvp-clikify {
  display: grid;
  gap: 0.35rem;
  margin: 0;
  padding: 0.8rem 0.9rem;
  border: 1px dashed color-mix(in srgb, var(--event-primary, #59306f) 30%, transparent);
  border-radius: 0.6rem;
  font-size: 0.88rem;
  line-height: 1.45;
  color: color-mix(in srgb, var(--event-text, #20232d) 85%, transparent);
}
.rsvp-clikify p {
  margin: 0;
}
.rsvp-clikify a {
  justify-self: start;
  font-weight: 600;
}
.rsvp-legal {
  margin: 0;
  font-size: 0.8rem;
  color: color-mix(in srgb, var(--event-text, #5c5368) 70%, transparent);
  line-height: 1.45;
  border-top: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 16%, transparent);
  padding-top: 0.9rem;
}
.rsvp-manual {
  display: grid;
  gap: 0.75rem;
}
.pass-card {
  background: var(--event-surface, color-mix(in srgb, var(--event-background, #ffffff) 88%, white));
  border: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 22%, transparent);
  border-radius: 1.25rem;
  padding: clamp(1.5rem, 6vw, 2.5rem);
  text-align: center;
  display: grid;
  gap: 0.75rem;
  justify-items: center;
  box-shadow: 0 1rem 3rem #3f245412;
}
.pass-event {
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.8rem;
  color: var(--event-primary, #6c3b86);
  font-weight: 700;
}
.pass-card h1 {
  margin: 0;
  font-size: clamp(1.5rem, 6vw, 2rem);
  line-height: 1.15;
}
.pass-count {
  margin: 0;
  font-weight: 700;
}
/* El fondo del QR se queda blanco: es lo que garantiza que una cámara lo lea. */
.pass-qr {
  background: white;
  padding: 0.75rem;
  border-radius: 0.75rem;
  border: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 16%, transparent);
  line-height: 0;
}
.pass-qr svg {
  width: min(14rem, 60vw);
  height: auto;
}
.pass-note {
  margin: 0;
  font-size: 0.85rem;
  color: color-mix(in srgb, var(--event-text, #5c5368) 70%, transparent);
}

/* La página ya envuelve el contenido en su propia tarjeta: sin esto, `main`
   dibuja una segunda caja blanca alrededor y se ven bordes duplicados. */
.rsvp-page main,
.pass-page main {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
}
.rsvp-page,
.pass-page {
  accent-color: var(--event-primary, #59306f);
}
.rsvp-page a,
.pass-page a {
  color: var(--event-primary, #59306f);
}
.pass-when {
  margin: 0;
  color: color-mix(in srgb, var(--event-text, #5c5368) 78%, transparent);
  font-size: 0.95rem;
}
.pass-where {
  margin: 0;
  font-weight: 600;
}

.rsvp-card button:disabled {
  background: color-mix(
    in srgb,
    var(--event-primary, #59306f) 22%,
    var(--event-background, #ffffff)
  );
  border-color: color-mix(
    in srgb,
    var(--event-primary, #59306f) 22%,
    var(--event-background, #ffffff)
  );
  color: color-mix(in srgb, var(--event-text, #5c5368) 70%, transparent);
  cursor: not-allowed;
}

/* —— Álbum: superficie del invitado ————————————————————————————————— */

.album-page {
  accent-color: var(--event-primary, #59306f);
}
/* Sin esto los enlaces salen en el azul del navegador, ajeno a la paleta. */
.album-page a {
  color: var(--event-primary, #6e315f);
}
.album-page main {
  width: min(52rem, calc(100% - 1rem));
  /* Sin esto el bloque calculaba su ancho pero se quedaba pegado a la
     izquierda, con todo el hueco sobrante a la derecha. */
  margin-inline: auto;
  background: var(--event-background, #fff9f4);
  border: none;
  box-shadow: none;
  padding: clamp(1rem, 3vw, 2.5rem);
}
.album {
  min-width: 0;
}
.album-window {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  margin-bottom: 1.5rem;
  padding: 1.15rem 0;
  border-bottom: 1px solid color-mix(in srgb, var(--event-text, #2b2028) 18%, transparent);
  color: var(--event-text, #2b2028);
}
.album-window > div {
  min-width: 0;
}
.album-window-icon {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.1rem;
}
.album-window h2 {
  margin: 0 0 0.65rem;
  font:
    600 clamp(1.05rem, 3vw, 1.25rem) / 1.4 Manrope,
    system-ui,
    sans-serif;
  text-wrap: balance;
}
.album-window p {
  margin: 0.45rem 0 0;
  line-height: 1.65;
  overflow-wrap: anywhere;
}
.album-window time {
  font-weight: 600;
}
.album-window .album-window-zone {
  font-size: 0.8rem;
}
.album-help {
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--event-text, #2b2028);
}
.album-footer {
  display: grid;
  gap: 0.5rem;
  margin-top: 2rem;
  border-top: 1px solid color-mix(in srgb, var(--event-text, #2b2028) 18%, transparent);
  padding-top: 1rem;
}
.album-footer p {
  margin: 0;
}
.album-page :is(button, .album-picker, .album-check) {
  touch-action: manipulation;
}
.album-page .link {
  min-height: 44px;
  white-space: normal;
  color: var(--event-text, #2b2028);
}
.album-page .dialog-head .link {
  min-width: 44px;
}
.album-head {
  text-align: center;
  margin-bottom: 1.5rem;
}
.album-head h1 {
  font-family: EventoSerif, Georgia, serif;
  color: var(--event-primary, #6e315f);
  margin: 0.25rem 0 0.5rem;
}
.album-lead {
  margin: 0;
  color: var(--event-text, #2b2028);
}
.album-upload {
  border: 1px solid color-mix(in srgb, var(--event-primary, #6e315f) 25%, transparent);
  border-radius: 1rem;
  padding: clamp(1rem, 4vw, 1.5rem);
  margin-bottom: 2rem;
  background: var(--event-surface, #ffffffb3);
}
.album-upload h2 {
  font-family: EventoSerif, Georgia, serif;
  color: var(--event-primary, #6e315f);
  margin-top: 0;
}
.album-legal {
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--event-text, #4a4250);
  padding-block: 0.75rem;
  border-block: 1px solid color-mix(in srgb, var(--event-text, #4a4250) 15%, transparent);
}
.album-check,
.album-field {
  display: block;
  margin: 0.75rem 0;
}
.album-check {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
  font-weight: 600;
  min-height: 44px;
  padding-block: 0.5rem;
  line-height: 1.5;
  cursor: pointer;
}
.album-check input {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  margin: 0.2rem 0 0;
  padding: 0;
}
.album-field span {
  display: block;
  font-size: 0.85rem;
  margin-bottom: 0.25rem;
}
.album-field input {
  width: 100%;
  min-height: 2.75rem;
  padding: 0 0.75rem;
  border: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 28%, transparent);
  border-radius: 0.6rem;
  font: inherit;
  box-sizing: border-box;
  background: var(--event-background, #fff9f4);
  color: var(--event-text, #2b2028);
  font-size: 1rem;
}
.album-field input::placeholder {
  color: color-mix(in srgb, var(--event-text, #2b2028) 70%, var(--event-background, #fff9f4));
}
.album-picker {
  position: relative;
  display: block;
  width: fit-content;
  max-width: 100%;
}
/* El `input[type=file]` nativo es imposible de alinear con el resto; se oculta
   y la etiqueta hace de botón, conservando su comportamiento y su foco. */
.album-picker input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.album-picker span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  line-height: 1.5;
  padding: 0.75rem 1.25rem;
  border-radius: 999px;
  background: var(--event-primary, #6e315f);
  color: var(--event-on-primary, white);
  font-weight: 700;
  cursor: pointer;
}
.album-picker:focus-within span {
  outline: 3px solid var(--event-accent, #c99a4a);
  outline-offset: 2px;
}
.album-picker input:disabled + span {
  background: color-mix(
    in srgb,
    var(--event-primary, #59306f) 22%,
    var(--event-background, #ffffff)
  );
  color: color-mix(in srgb, var(--event-text, #5c5368) 70%, transparent);
  cursor: not-allowed;
}
.album-queue {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}
.album-queue li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.25rem 0.75rem;
  align-items: center;
  font-size: 0.9rem;
  border-bottom: 1px solid color-mix(in srgb, var(--event-primary, #59306f) 16%, transparent);
  padding-bottom: 0.5rem;
}
.album-queue-name {
  overflow-wrap: anywhere;
  grid-column: 1 / -1;
  font-weight: 600;
}
.album-queue-state {
  color: var(--event-text, #5c5368);
  font-size: 0.9rem;
  overflow-wrap: anywhere;
  line-height: 1.6;
}
.album-queue li[data-state='error'] .album-queue-state {
  color: color-mix(in srgb, #e2574c 70%, var(--event-text, #20232d));
}
/* Esperar no es fallar: una foto que se reintenta sola no debe alarmar. */
.album-queue li[data-state='waiting'] .album-queue-state {
  color: var(--event-text, #20232d);
}
.album-retry-all {
  margin-top: 0.75rem;
  min-height: 44px;
  width: 100%;
  /* El único bloque que no tenía un solo token: violeta del panel sobre blanco,
     que sobre la paleta de un evento se veía de otra aplicación. */
  border: 1px solid var(--event-primary, #7f00fd);
  background: var(--event-surface, #ffffff);
  color: var(--event-primary, #4a1580);
  border-radius: 0.75rem;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.album-queue progress {
  grid-column: 1 / -1;
  width: 100%;
  height: 0.4rem;
}
.album-note {
  font-size: 0.9rem;
  color: var(--event-text, #4a4250);
  line-height: 1.65;
}
.album-note-error {
  color: color-mix(in srgb, #e2574c 70%, var(--event-text, #20232d));
}
.album-empty {
  background: var(--event-surface, #ffffffb3);
  border-radius: 0.9rem;
  padding: 1.5rem;
  text-align: center;
}
.album-gallery-block h2 {
  font-family: EventoSerif, Georgia, serif;
  color: var(--event-primary, #6e315f);
}
.album-gallery {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
  gap: 0.5rem;
}
/* La hoja del panel enmarca cada `li` como tarjeta. Aquí las fotos forman un
   mosaico: el marco y su relleno robarían espacio en la pantalla del móvil. */
.album-gallery li,
.album-queue li {
  border: none;
  border-radius: 0;
  padding: 0;
}
.album-gallery button {
  display: block;
  width: 100%;
  padding: 0;
  border: none;
  border-radius: 0.6rem;
  overflow: hidden;
  background: none;
  cursor: pointer;
}
.album-gallery img {
  display: block;
  width: 100%;
  /* `height: auto` es imprescindible: los atributos width/height del propio
     `img` —que evitan el salto de maquetación mientras carga— se aplican como
     altura y, sin esto, ganarían al `aspect-ratio` y estirarían la miniatura. */
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}
.album-full {
  display: block;
  width: 100%;
  max-height: 70vh;
  object-fit: contain;
}
@media (max-width: 480px) {
  .album-picker {
    width: 100%;
  }
  .album-window {
    display: block;
  }
  .album-window-icon {
    margin-bottom: 0.65rem;
  }
  .album-queue li {
    grid-template-columns: minmax(0, 1fr);
  }
  .album-queue .link {
    justify-self: start;
  }
}
@media (prefers-reduced-motion: reduce) {
  .album-gallery button {
    transition: none;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* ── Invitación personal ──────────────────────────────────────────────────
   La franja que la familia ve de sí misma, entre la portada y el resto de la
   invitación. Va con la paleta del evento, no con la del panel: antes el
   enlace personal abría un formulario lila que no se parecía a nada de lo que
   el cliente aprobó. */
.xv-personal {
  max-width: 64rem;
  margin: auto;
  padding: clamp(2rem, 7vw, 3.5rem) clamp(1.25rem, 5vw, 3rem) 0;
  text-align: center;
}
.xv-personal h2 {
  font-size: clamp(1.9rem, 6vw, 3rem);
  margin: 0.2em 0 0.35em;
  color: var(--event-primary);
  text-wrap: balance;
}
.xv-personal-seats {
  margin: 0;
  font-size: 1.05rem;
}
.xv-personal-state {
  margin: 0.35rem 0 0;
  opacity: 0.75;
}
/* El botón que lleva a responder: relleno sólido y área táctil cómoda, porque
   casi todo el mundo lo pulsa con el pulgar en un teléfono. */
.xv-personal-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  margin-top: 1.25rem;
  padding: 0.75rem 1.75rem;
  border-radius: 999px;
  background: var(--event-primary);
  color: var(--event-background);
  font-weight: 800;
  text-decoration: none;
}
.xv-personal-cta:focus-visible {
  outline: 3px solid var(--event-accent);
  outline-offset: 3px;
}

/* ── Respuesta dentro de la invitación ──────────────────────────────────── */
.xv-rsvp {
  scroll-margin-top: 1rem;
}
.xv-rsvp h2 {
  font-size: clamp(2rem, 7vw, 3.5rem);
}
.xv-rsvp .xv-eyebrow {
  text-align: center;
}
.xv-rsvp-hint {
  margin: 0.25rem 0 1.5rem;
  text-align: center;
  opacity: 0.8;
}
/* El formulario conserva su maquetación, pero deja de traer el fondo del panel:
   aquí vive dentro de la invitación. */
.xv-rsvp .rsvp-card {
  max-width: 34rem;
  margin: 0 auto;
  background: var(--event-surface, color-mix(in srgb, var(--event-background) 88%, white));
  border: 1px solid color-mix(in srgb, var(--event-primary) 25%, transparent);
}

/* Los enlaces al pie del álbum y de la invitación personal: mismo criterio. */
.album-note a,
.album-empty a,
.rsvp-hint a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
}

/* ── Vista previa del operador ────────────────────────────────────────────
   La previa es una caja dentro del editor, no una ventana: las unidades de
   viewport de la invitación miden la pantalla del operador y no la caja, así
   que el nombre de la quinceañera aparecía cortado y la previa mentía sobre
   cómo se ve en un teléfono. Dentro de la previa las mismas medidas se
   expresan contra el ancho del contenedor. */
.preview-frame {
  container-type: inline-size;
}
.editor-preview .xv-hero {
  padding: clamp(2rem, 12cqw, 9rem) 1.5rem;
  min-height: 0;
}
.editor-preview .xv-hero h1 {
  font-size: clamp(2.5rem, 16cqw, 8.5rem);
}
.editor-preview .xv-welcome {
  font-size: clamp(0.95rem, 3.6cqw, 1.3rem);
}
.editor-preview .xv-section {
  padding: clamp(2rem, 10cqw, 7rem) clamp(1rem, 5cqw, 3rem);
}
.editor-preview .xv-section h2 {
  font-size: clamp(1.75rem, 8cqw, 5rem);
}
.editor-preview .xv-personal {
  padding: clamp(1.5rem, 7cqw, 3.5rem) clamp(1rem, 5cqw, 3rem) 0;
}
.editor-preview .xv-personal h2 {
  font-size: clamp(1.5rem, 6cqw, 3rem);
}

/* ── Pase con la identidad del evento ─────────────────────────────────────
   El pase se pintaba con colores fijos ajenos a la paleta y, abierto desde
   WhatsApp, parecía de otro servicio. Cuando la página conoce el evento pone su
   paleta en el body (`data-evento-theme`); sin evento se queda como estaba. */
.pass-page[data-evento-theme] {
  background: color-mix(in srgb, var(--event-background) 90%, var(--event-accent));
  color: var(--event-text);
  accent-color: var(--event-primary);
}
.pass-page[data-evento-theme] a,
.pass-page[data-evento-theme] .pass-event {
  color: var(--event-primary);
}
.pass-page[data-evento-theme] .pass-card {
  background: var(--event-background);
  border-color: color-mix(in srgb, var(--event-accent) 45%, transparent);
}
.pass-page[data-evento-theme] .pass-card h1 {
  font-family: EventoSerif, Georgia, serif;
  color: var(--event-primary);
}
.pass-page[data-evento-theme] .pass-when,
.pass-page[data-evento-theme] .pass-note {
  color: var(--event-text);
}

/* Aviso de la vista previa: vive aquí porque la vista previa de un borrador sólo
   carga la hoja del evento, sin el estilo del panel. */
.preview-banner {
  position: sticky;
  top: 0;
  z-index: 5;
  padding: 0.6rem 1rem;
  text-align: center;
  background: #211c2d;
  color: white;
  font:
    700 0.8rem Manrope,
    system-ui,
    sans-serif;
  letter-spacing: 0.04em;
}
