/* ══════════════════════════════════════════════════════════════
   LIBRO DE FIRMAS · FastInvit
   Paleta y tipografías de marca. Prefijo .lf- para no chocar con nada.

   Las cuatro tipografías —Montserrat, Great Vibes, Cinzel y Patrick Hand—
   salen de /fonts/fonts.css, que lo genera FontCatalogService a
   partir de data/fonts.json y que esta página ya incluye. Aquí no se declara
   ningún @font-face: duplicarlo dejaría dos definiciones de la misma familia
   que se pueden desincronizar al cambiar un fichero de fuente.
   ══════════════════════════════════════════════════════════════ */

:root{
  --lf-primary:var(--color-primary,#4e6958);
  --lf-gold:#c5a059;
  --lf-cream:#f7f2e6;
  --lf-ink:#3a4640;
  /* El lino de la tapa, en una var porque lo pintan tres superficies: la cara de
     fuera, la de dentro y la hoja izquierda a la que esta releva al abrirse. */
  --lf-linen:url("/images/decor/linen_texture.webp");
  /* La guarda: el mismo tejido, pero a la sombra del libro abierto y sin la luz
     cenital que recibe la cara de fuera. Lo oscuro cae hacia el lomo, que en esa
     mitad es el borde derecho — el mismo lado por el que entra el pliegue. */
  --lf-linen-inside:
    linear-gradient(270deg,rgba(86,68,44,.3),rgba(86,68,44,.04) 42%),
    radial-gradient(120% 90% at 40% 45%,rgba(255,250,238,.16),rgba(78,60,38,.16) 88%),
    #cabd9f var(--lf-linen) center/cover no-repeat;
  /* Marco de la tarjeta de la tapa. Es también el color del texto impreso en
     ella: un solo tono para el passe-partout y las letras, como en la papelería
     de verdad, donde todo sale de la misma tinta. */
  --lf-mat:#7a939b;
  --lf-e:cubic-bezier(.6,.04,.18,1);
}
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;height:100%}
body{
  font-family:'Montserrat',system-ui,sans-serif;background:#0e1410;color:var(--lf-cream);
  overflow:hidden;-webkit-tap-highlight-color:transparent;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--lf-gold);text-decoration:none}a:hover{color:#e0c793}
button{font-family:inherit;cursor:pointer}

/* ── Fondo: imagen borrosa + velo + polvo dorado ── */
.lf-bg{position:fixed;inset:0;z-index:0;overflow:hidden}
.lf-bg-photo{
  position:absolute;inset:-6%;
  background:var(--lf-bg-image,radial-gradient(70% 55% at 28% 26%,#54705c,transparent 62%)),
             radial-gradient(60% 50% at 78% 72%,#6b5a3a,transparent 60%),
             linear-gradient(150deg,#25332a,#141c17 65%);
  background-size:cover;background-position:center;
  filter:blur(26px) saturate(.86);transform:scale(1.06);
}
.lf-bg-veil{
  position:absolute;inset:0;
  background:radial-gradient(120% 85% at 50% 34%,rgba(28,40,32,.42),rgba(11,17,13,.9) 76%);
}
.lf-bg-veil::after{
  content:"";position:absolute;inset:0;
  background:radial-gradient(38% 30% at 50% 44%,rgba(197,160,89,.15),transparent 70%);
}
.lf-dust{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}

/* ── Escenario ── */
.lf-stage{
  position:relative;z-index:1;height:100%;
  display:grid;grid-template-rows:1fr auto;align-items:center;justify-items:center;
  padding:20px 16px 0;
}
/* El libro vive dentro de un envoltorio que aporta la perspectiva. Las flechas
   son hermanas del libro, fuera de su contexto 3D: así la hoja que gira nunca
   las tapa, por más que en 3D quede por delante. */
.lf-bookwrap{
  position:relative;display:grid;justify-items:center;width:100%;
  --bw:min(94vw,calc(70vh * 1.44));perspective:2200px;
}
.lf-book{
  position:relative;transform-style:preserve-3d;
  width:var(--bw);aspect-ratio:1.44/1;
  --sx:-25%;
  transform:translate(var(--sx),0);
  transition:transform 1.5s var(--lf-e),translate .6s var(--lf-e);
}
.lf-book.is-open{transform:none}
/* Cerrado, el libro flota apenas: es lo único que se mueve en la pantalla y
   pide que lo toquen. Va en `translate` y NO en `transform`: el transform es el
   que hace el corrimiento al abrir, y si los dos se pelean el giro se corta. */
@keyframes lf-float{0%,100%{translate:0 0}50%{translate:0 -22px}}
.lf-book:not(.is-open):not(.is-closing){animation:lf-float 3.8s ease-in-out infinite}

/* ── Spread: dos hojas + lomo ── */
.lf-spread{
  position:absolute;inset:0;display:grid;grid-template-columns:1fr 1fr;
  border-radius:4px 7px 7px 4px;overflow:hidden;
  box-shadow:0 42px 90px rgba(6,10,8,.62),0 6px 20px rgba(6,10,8,.4);
  opacity:0;
}
/* La transición vive AQUÍ y no en la regla de arriba a propósito: el retardo es
   para entrar —el spread aparece cuando la tapa ya se ha apartado—, y al salir
   sobra. Con ella en la regla base, al terminar de cerrarse el libro el spread se
   quedaba casi segundo y medio apagándose detrás de la tapa: por la izquierda, que
   es donde asoma más allá de ella, se veía el fantasma del libro abierto. */
.lf-book.is-open .lf-spread{opacity:1;transition:opacity .8s ease .55s}
/* Cerrando: el spread se mantiene visible hasta que la tapa aterriza encima
   (si se apagara antes, se vería el fondo en la mitad derecha), pero la hoja
   izquierda marrón se va enseguida — se la lleva la tapa — mientras el canto
   todavía la tapa. Si no, queda como una segunda tapa abierta ahí tirada. */
.lf-book.is-closing .lf-spread{opacity:1}
.lf-book.is-closing .lf-page--l{opacity:0;transition:opacity .25s linear}
.lf-page{
  position:relative;overflow:hidden;background:var(--lf-cream);container-type:inline-size;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='p'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/></filter><rect width='180' height='180' filter='url(%23p)' opacity='.42'/></svg>");
  background-size:180px;background-blend-mode:multiply;
}
.lf-page--l{box-shadow:inset -26px 0 30px -24px rgba(58,44,24,.62)}
.lf-page--r{box-shadow:inset 26px 0 30px -24px rgba(58,44,24,.62)}
/* La cara interna de la tapa NO es un elemento 3D dentro del libro: es el
   fondo de la hoja izquierda. Así la hoja que gira pasa por encima como sobre
   cualquier otra hoja, sin pelearse en profundidad con la tapa. */
/* La mitad izquierda no existe hasta que la tapa 3D terminó de aterrizar sobre
   ella y se retiró. Si aparece antes, se ven dos caras marrones al mismo tiempo:
   la que gira y una quieta debajo. */
.lf-book:not(.cover-done) .lf-page--l{visibility:hidden}
/* Sin firma no se pinta el guion suelto que pone ::before. Aplica también a las
   firmas de invitados, aunque ahí el nombre nunca llega vacío (cae en "Un invitado"). */
.lf-pol-name:empty{display:none}
/* La hoja izquierda cuando hace de guarda. background-blend-mode NO va en el
   atajo `background`, así que sin reponerlo a normal se queda el multiply que
   .lf-page usa para el grano del papel: multiplicaría las tres capas del lino y
   la hoja saldría bastante más oscura que la cara 3D a la que releva. Eso era el
   escalón de color al terminar de abrirse (y el aclarón al empezar a cerrarse). */
.lf-page.is-cover{
  background:var(--lf-linen-inside);
  background-blend-mode:normal;
  box-shadow:inset -26px 0 34px -24px rgba(38,26,10,.75);
}
/* Sombra del lomo: capa plana dentro del spread. La hoja que gira le pasa por
   delante, igual que en un libro de verdad. */
.lf-spine{display:none}

/* ── Polaroids ── */
.lf-pol{
  position:absolute;margin:0;z-index:1;background:#fffdf7;padding:4% 4% 0;container-type:inline-size;
  box-shadow:0 10px 22px rgba(40,32,16,.24),0 1px 3px rgba(40,32,16,.2);
  transform:rotate(var(--r,0deg));
}
.lf-pol--in{animation:lf-pol-in .6s var(--lf-e) both;animation-delay:var(--d,0s)}
@keyframes lf-pol-in{
  from{opacity:0;transform:rotate(var(--r,0deg)) translateY(14px) scale(.96)}
  to{opacity:1;transform:rotate(var(--r,0deg))}
}
.lf-pol-shot{
  position:relative;aspect-ratio:3/4;overflow:hidden;
  background:linear-gradient(150deg,#e6ddc9,#cfc3a8 60%,#bdb197);
}
/* Foto apaisada: la ventana se acuesta en vez de recortar de más. */
.lf-pol-shot.is-wide{aspect-ratio:4/3}
.lf-pol-shot img{width:100%;height:100%;object-fit:cover;display:block}
.lf-pol-shot::after{
  content:"";position:absolute;inset:0;box-shadow:inset 0 0 0 1px rgba(90,74,48,.14),inset 0 -22px 34px rgba(90,74,48,.1);
}
.lf-pol--nophoto .lf-pol-shot{display:none}
.lf-pol-cap{padding:6% 4% 8%;text-align:center}
.lf-pol--nophoto .lf-pol-cap{padding:16% 8% 14%}
.lf-pol-msg{
  font-family:'Patrick Hand',cursive;color:#3e4a41;margin:0;
  font-size:clamp(8px,8.4cqw,17px);line-height:1.34;text-wrap:pretty;
  overflow-wrap:break-word;
  display:-webkit-box;-webkit-line-clamp:5;-webkit-box-orient:vertical;overflow:hidden;
}
.lf-pol-name{
  display:block;margin:.55em 0 0;font-family:'Patrick Hand',cursive;
  font-size:clamp(7px,6.4cqw,13px);color:#8b7f66;
}
.lf-pol-name::before{content:"— "}
.lf-pol.is-new{outline:2px solid var(--lf-gold);outline-offset:5px}
.lf-page .lf-pol{cursor:zoom-in}
.lf-page .lf-pol:focus-visible{outline:2px solid var(--lf-gold);outline-offset:5px}

@keyframes lf-hint{0%,100%{opacity:.55}50%{opacity:.95}}
.lf-zoom-min{display:flex;align-items:center;gap:4px}
#lf-zoom-del.is-armed{color:#e3a58c}

/* ══════ Zoom de una firma ══════ */
.lf-zoom{
  position:fixed;inset:0;z-index:55;display:flex;align-items:center;justify-content:center;
  padding:20px;background:rgba(9,14,11,.74);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  opacity:0;pointer-events:none;transition:opacity .34s ease;
}
.lf-zoom.is-on{opacity:1;pointer-events:auto}
.lf-zoom-inner{display:flex;flex-direction:column;align-items:center;gap:14px}
/* La tarjeta crece desde su lugar en la hoja: el transform de salida lo calcula
   el script y esta transición lo lleva hasta acá. */
.lf-pol--big{
  position:static;width:min(330px,78vw,calc((100dvh - 190px) / 1.62));
  transform:rotate(-1deg);transition:transform .46s var(--lf-e);
  box-shadow:0 30px 66px rgba(6,10,8,.55);
}
.lf-pol--big .lf-pol-msg{display:block;-webkit-line-clamp:unset;font-size:clamp(15px,5cqw,20px)}
.lf-pol--big .lf-pol-name{font-size:clamp(12px,3.9cqw,15px)}
.lf-zoom-acts{display:flex;flex-direction:column;align-items:center;gap:6px}
#lf-zoom-edit{flex:0 0 auto;padding:0 26px}
.lf-zoom-note{margin:0;font-size:10.5px;letter-spacing:.16em;color:rgba(240,232,214,.5);text-align:center}
.lf-zoom-x{
  background:none;border:0;color:rgba(240,232,214,.6);font-size:10.5px;letter-spacing:.24em;
  text-transform:uppercase;padding:10px 14px;min-height:44px;transition:color .2s;
}
.lf-zoom-x:hover{color:#f0e8d6}

/* ── Encabezado de hoja ── */
.lf-page-head{position:absolute;left:8%;right:8%;top:4%;text-align:center;z-index:0;pointer-events:none}
.lf-page-head .t{font-family:'Great Vibes',cursive;font-size:clamp(17px,2.4cqw,36px);color:var(--lf-primary);margin:0;line-height:1}
.lf-page-head .r{width:38px;height:1px;background:var(--lf-gold);margin:.5em auto}
.lf-page-head .d{margin:0;font-size:clamp(7px,.78cqw,11px);letter-spacing:.34em;color:#a8956a}
.lf-page-empty{
  position:absolute;inset:0;display:grid;place-content:center;gap:10px;justify-items:center;
  text-align:center;padding:14%;color:#a99e85;
}
.lf-page-empty p{margin:0;font-family:'Patrick Hand',cursive;font-size:clamp(13px,1.5cqw,20px);color:#9d947c}
.lf-page-foot{position:absolute;left:0;right:0;bottom:3.5%;text-align:center;font-size:clamp(8px,.8cqw,11px);letter-spacing:.3em;color:#b3a789;z-index:0;pointer-events:none}

/* ── Tapa ── */
.lf-cover{
  position:absolute;top:0;bottom:0;left:50%;right:0;z-index:6;
  transform-origin:left center;transform-style:preserve-3d;
  will-change:transform;
  transition:transform 1.5s var(--lf-e);
}
/* Abierta, la tapa ya no participa del libro: se apaga y su lugar lo ocupa
   el lino de la hoja izquierda. */
.lf-book.is-open .lf-cover{transform:rotateY(-178deg)}
.lf-book.is-open .lf-cover{pointer-events:none}
/* Ya abierta, la cara de afuera no debe verse nunca (algunos motores
   filtran mal el backface con overflow+border-radius). */
.lf-book.cover-flat .lf-cover-face:not(.lf-cover-face--in){opacity:0}
/* Sin transición de opacidad: el encendido y apagado de esta cara ocurre con el
   canto de perfil, donde el backface ya la oculta. Un fade, en cambio, la deja
   traslúcida mientras gira y se ve el spread a través de la tapa. */
.lf-cover-face{
  position:absolute;inset:0;backface-visibility:hidden;-webkit-backface-visibility:hidden;
  border-radius:3px 8px 8px 3px;overflow:hidden;
  /* Lino de verdad, no un degradado que lo imite: el tejido es lo que hace que la
     tapa se lea como tela encuadernada y no como cartón pintado. Los dos degradados
     de encima ponen el volumen que una foto plana no trae —luz cayendo de arriba y
     la sombra del lomo, que en esta mitad del libro es el borde izquierdo—. El color
     de debajo es el del propio lino: se ve mientras la imagen carga. */
  background:
    radial-gradient(118% 84% at 50% 2%,rgba(255,253,246,.4),rgba(255,253,246,0) 48%),
    linear-gradient(90deg,rgba(104,84,54,.26),rgba(104,84,54,0) 26%),
    #d8cbb0 var(--lf-linen) center/cover no-repeat;
  box-shadow:0 40px 80px rgba(6,10,8,.6),inset 0 0 0 1px rgba(255,252,242,.42);
  display:grid;place-items:center;
}
/* La cara interna de la tapa aterriza en la mitad izquierda con rotación neta
   ~0° (no queda espejada). Se pinta idéntica a .lf-page.is-cover — mismo fondo,
   mismo pliegue en el borde derecho, mismo redondeo — para que el relevo entre
   la tapa 3D y la hoja de abajo no se note. */
.lf-cover-face--in{
  transform:rotateY(180deg);border-radius:4px 0 0 4px;
  background:var(--lf-linen-inside);
  box-shadow:0 40px 80px rgba(6,10,8,.6),inset -26px 0 34px -24px rgba(38,26,10,.75);
}
/* La etiqueta son dos piezas: el marco de color (esto) y el papel impreso que va
   dentro. El marco es el borde que asoma por los cuatro lados, así que su padding
   ES su grosor —en %, o sea proporcional a la tapa, para que no se engorde en un
   móvil ni desaparezca en un monitor grande—.
   Y es el contenedor de consulta del texto: las letras se miden contra el ancho de
   la tarjeta y no contra la ventana, que es lo único que mantiene la proporción
   igual en las dos maquetas (en móvil el libro es mucho más ancho que la pantalla). */
.lf-cover-label{
  position:relative;z-index:2;width:59%;padding:3.1%;
  container-type:inline-size;
  background:var(--lf-mat);
  box-shadow:0 18px 34px rgba(52,36,14,.28),0 2px 6px rgba(52,36,14,.16);
}
.lf-cover-card{
  /* La tarjeta es una pieza de papelería, no una caja que se ajusta al texto: se le
     fija la proporción (retrato, como una tarjeta impresa) y el contenido se centra
     dentro. min-content es la salvaguarda para un nombre muy largo, que hace crecer
     la tarjeta en vez de desbordarse por abajo. */
  aspect-ratio:1/1.2;min-height:min-content;
  display:grid;align-content:center;
  padding:9%;text-align:center;
  background:#fdfcfa url("/images/decor/paper-texture.webp") center/cover no-repeat;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.5);
}
.lf-cover-card h1{
  margin:0;font-family:'Great Vibes',cursive;font-weight:400;
  font-size:clamp(18px,11.5cqw,46px);line-height:1.12;color:var(--lf-mat);
  text-wrap:balance;
}
.lf-cover-card .d{
  margin:.9em 0 0;font-family:'Cinzel',serif;
  font-size:clamp(8px,3.5cqw,14px);letter-spacing:.24em;color:var(--lf-mat);
}
/* La ramita, acostada. El asset es vertical (310×800) y lo tumba el CSS, así que el
   hueco que reserva es el de la imagen YA girada: la caja tiene la proporción
   invertida y la hoja va dentro con el ancho del alto de la caja (310/800 = 38.75%),
   girada sobre su centro. Sin esto el navegador reservaría el alto de la imagen de
   pie —el triple— y empujaría media tarjeta fuera del marco. */
.lf-cover-sprig{position:relative;width:62%;aspect-ratio:800/310;margin:16% auto 0}
.lf-cover-sprig img{
  position:absolute;top:50%;left:50%;width:38.75%;
  transform:translate(-50%,-50%) rotate(90deg);
}
/* Sobre el lino claro el texto tiene que ser oscuro: en crema —que es lo que pedía
   la tapa kraft— se volvería invisible. */
.lf-cover-hint{
  animation:lf-hint 4.4s ease-in-out infinite;position:absolute;left:0;right:0;bottom:7%;text-align:center;font-size:clamp(9px,1.05vmin,11px);font-weight:700;letter-spacing:.34em;text-transform:uppercase;color:#4a3a26;z-index:3}

/* ── Libro sellado: hasta el día del evento se ve pero no se abre ──
   (GuestbookService.isSealed). La pista de la tapa dice cuándo se abre, con un candado
   delante que adelanta lo que pasa al tocarla: la tapa intenta abrirse —hacia el mismo
   lado que al abrirse de verdad—, el cierre la frena y se asienta con un amago más
   pequeño. Lo dispara guestbook.js poniendo .is-nudge en el libro. */
.lf-cover-hint--sealed{display:flex;align-items:center;justify-content:center;gap:.7em}
.lf-cover-hint--sealed svg{
  width:1.3em;height:1.3em;flex:none;fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round;
}
/* Sin transición en la tapa sellada: nunca gira, y una transición de transform le
   ganaría al temblor —en la cascada las transiciones mandan sobre las animaciones— y
   lo dejaría quieto si un toque llega a mitad del anterior. */
.lf-book.is-sealed .lf-cover{transition:none}
/* Dos rebotes, el segundo menor, en 0,8 s. Es el punto medio entre las dos versiones que
   hubo: tres rebotes en 0,7 s con ±8 px de temblor se leían nerviosos, y un solo tirón en
   0,9 s con ease-in-out se quedaba blando. La curva va POR TRAMO y es la media de las dos:
   cada tramo sale con algo de impulso —el golpe contra el cierre— pero llega frenado al
   extremo, en vez de salir disparado (muelle) o salir parado (desgana). */
@keyframes lf-sealed-nudge{
  0%  {transform:rotateY(0);      translate:0 0}
  22% {transform:rotateY(-17deg); translate:-3px 0}
  44% {transform:rotateY(-2deg);  translate:5px 0}
  64% {transform:rotateY(-7deg);  translate:-4px 0}
  84% {transform:rotateY(0);      translate:1px 0}
  100%{transform:rotateY(0);      translate:0 0}
}
.lf-book.is-nudge .lf-cover{animation:lf-sealed-nudge .8s cubic-bezier(.36,.3,.49,1)}
/* La fecha se asoma a la vez: es la respuesta a "¿por qué no se abre?". Va DETRÁS del
   latido de siempre en la lista para ganarle mientras dura sin reiniciarlo, y sin
   0%/100% para salir y volver desde donde el latido esté, sin salto. */
@keyframes lf-hint-nudge{32%{opacity:1;scale:1.07}}
.lf-book.is-nudge .lf-cover-hint{animation:lf-hint 4.4s ease-in-out infinite,lf-hint-nudge .8s ease}

/* ── Hoja que gira ── */
/* El contenedor de consulta de las dos caras vive AQUÍ, en la hoja, y NO en la cara
   —aunque las dos midan exactamente lo mismo—. Es un fallo de WebKit y es el que
   dejaba la hoja girando vacía en iOS: la cara es el bloque contenedor de las
   polaroids, que se colocan en % (left, top y sobre todo WIDTH), y esos porcentajes
   se resuelven a cero cuando ese mismo bloque es contenedor de consulta y saca su
   ancho de los offsets. Por eso el encabezado y el número de página —que se colocan
   con left/right, no con width— sí salían, y ni una sola tarjeta lo hacía: esa
   asimetría es la firma del fallo. La hoja fija no lo sufre porque es celda de una
   grilla y su ancho no viene de offsets.
   Subirlo aquí no cambia ningún tamaño: la hoja y la cara tienen el mismo ancho, así
   que las cq de dentro (encabezado, pie, mensaje de hoja vacía) valen lo mismo. */
.lf-flip{
  position:absolute;top:0;bottom:0;z-index:7;transform-style:preserve-3d;
  container-type:inline-size;
  transition:transform .85s var(--lf-e);
}
.lf-flip--next{left:50%;right:0;transform-origin:left center}
.lf-flip--prev{left:0;right:50%;transform-origin:right center}
.lf-flip--next.go{transform:rotateY(-178deg)}
.lf-flip--prev.go{transform:rotateY(178deg)}
/* Ancho y alto explícitos en vez de inset:0 —es lo mismo, la cara es su bloque
   contenedor— y sin contención propia: la trae .lf-page y aquí hay que quitarla,
   que es justo el fallo de arriba. */
.lf-flip-face{
  position:absolute;top:0;left:0;width:100%;height:100%;
  overflow:hidden;backface-visibility:hidden;-webkit-backface-visibility:hidden;
  container-type:normal;
  /* Transform PROPIO —aunque sea la identidad— y petición explícita de capa. La cara
     trasera ya tenía el suyo (el rotateY(180deg) de más abajo) y la delantera se
     quedaba sin ninguno: dentro de un contexto preserve-3d eso es lo que decide, en
     WebKit, si el elemento se rasteriza en su propia capa o cuelga de la del padre.
     Y la del padre está girando, así que su contenido no se llegaba a pintar: la hoja
     salía en blanco aun teniendo las tarjetas en el DOM, con su tamaño y sus fotos ya
     cargadas (medido en el iPhone, no supuesto).
     Solo se notaba al avanzar porque en el móvil el libro es más ancho que la pantalla
     y la mitad izquierda queda casi fuera: hacia adelante la única cara que pisa la
     zona visible es la delantera, y hacia atrás la trasera —que nunca falló—.
     Los .001 grados NO son adorno: rotateY(0deg), translateZ(0) y translate3d(0,0,0)
     los colapsa el navegador a una matriz 2D, que es indistinguible de no tener
     transform y no serviría de nada. Con un ángulo mínimo pero real la matriz sale
     3D —comprobado— y la cara entra en el mismo camino de composición que su
     hermana. Visualmente son cero: una milésima de grado. */
  transform:rotateY(.001deg);
  will-change:transform;
}
/* La cara de la hoja tapa a la página estática desde el frame cero: si no lleva
   la misma sombra de pliegue, el pliegue se apaga al empezar el giro y vuelve
   al terminar. Va idéntica a la de las hojas fijas.
   La cara trasera termina con rotación neta ~0° (180° propios + los -178° del
   giro), o sea NO queda espejada: aterriza en la mitad opuesta y necesita la
   sombra en el borde contrario al de la cara frontal. */
.lf-flip--next .lf-flip-face{border-radius:0 7px 7px 0;box-shadow:inset 26px 0 30px -24px rgba(58,44,24,.62),0 0 30px rgba(40,32,16,.22)}
.lf-flip--next .lf-flip-face--back{border-radius:4px 0 0 4px;box-shadow:inset -26px 0 30px -24px rgba(58,44,24,.62),0 0 30px rgba(40,32,16,.22)}
.lf-flip--prev .lf-flip-face{border-radius:4px 0 0 4px;box-shadow:inset -26px 0 30px -24px rgba(58,44,24,.62),0 0 30px rgba(40,32,16,.22)}
.lf-flip--prev .lf-flip-face--back{border-radius:0 7px 7px 0;box-shadow:inset 26px 0 30px -24px rgba(58,44,24,.62),0 0 30px rgba(40,32,16,.22)}
.lf-flip-face--back{transform:rotateY(180deg)}
/* El relevo entre las dos caras NO puede confiarse solo a backface-visibility:
   WebKit no la respeta cuando la cara lleva overflow + border-radius —lo mismo que
   ya obliga a apagar a mano la cara de fuera de la tapa, ver .lf-book.cover-flat— y
   en iOS se veían las DOS a la vez: los mensajes de las dos páginas superpuestos
   mientras la hoja gira, y hacia adelante mandando la de detrás, que aún va vacía.
   Así que se apaga a mano la que no toca.

   Va con @keyframes y no con una transición de visibility a propósito: una
   animation IMPONE el valor mientras dura, sin depender de cuál era el valor
   anterior de la cara. Con transición hace falta que el "desde" sea el correcto, y
   eso es una suposición frágil sobre un elemento que se acaba de insertar.

   El corte va en el 41,5% y no en la mitad porque el giro NO es lineal: con
   --lf-e (cubic-bezier(.6,.04,.18,1)) la hoja está a 90° a esa altura del tiempo
   —medido, no estimado— mientras que a mitad de tiempo ya va por 130°. Los 90° son
   el único instante en el que se puede cambiar de cara sin que se note, porque es
   cuando el canto está de perfil y no se ve ninguna de las dos. Si algún día se
   cambia el easing o la duración del giro, hay que recalcular este número.

   Los dos fotogramas pegados (41,49 / 41,5) son a propósito: visibility no
   interpola como un número, así que el salto se hace con dos claves contiguas.
   En los motores que sí filtran el backface esto no cambia nada: apaga lo que ya
   estaba oculto de todos modos. */
@keyframes lf-face-front{0%,41.49%{visibility:visible}41.5%,100%{visibility:hidden}}
@keyframes lf-face-back{0%,41.49%{visibility:hidden}41.5%,100%{visibility:visible}}
.lf-flip-face--back{visibility:hidden}
.lf-flip.go .lf-flip-face:not(.lf-flip-face--back){animation:lf-face-front .85s linear forwards}
.lf-flip.go .lf-flip-face--back{animation:lf-face-back .85s linear forwards}

/* ── Navegación ── */
.lf-nav{
  position:absolute;top:50%;transform:translateY(-50%);z-index:40;
  width:46px;height:46px;border-radius:50%;border:1px solid rgba(240,232,214,.28);
  background:rgba(18,26,20,.5);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);
  color:#f0e8d6;display:grid;place-items:center;
  opacity:0;pointer-events:none;
  transition:opacity .6s ease .9s,background .25s,border-color .25s;
}
.lf-stage.is-open .lf-nav{opacity:1;pointer-events:auto}
.lf-nav:hover{background:rgba(18,26,20,.76);border-color:rgba(197,160,89,.6)}
.lf-nav svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.7}
.lf-nav--prev{left:max(10px,calc(50% - var(--bw) / 2 - 62px))}
.lf-nav--next{right:max(10px,calc(50% - var(--bw) / 2 - 62px))}
.lf-nav[disabled]{opacity:.24;pointer-events:none}
.lf-bar{
  display:flex;flex-direction:column;align-items:center;gap:12px;padding:16px 0 22px;
  font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:rgba(240,232,214,.6);
  opacity:0;transition:opacity .7s ease .8s;
}
.lf-stage.is-open .lf-bar{opacity:1}
.lf-bar b{font-weight:500;color:rgba(240,232,214,.9);letter-spacing:.28em}
.lf-sign{
  display:inline-flex;align-items:center;gap:9px;border:0;
  background:var(--lf-gold);color:#211a0c;font-size:12px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;padding:14px 24px;border-radius:100px;
  box-shadow:0 12px 30px rgba(197,160,89,.3);transition:transform .2s,box-shadow .2s;
  min-height:44px;
}
.lf-sign:hover{transform:translateY(-2px);box-shadow:0 16px 36px rgba(197,160,89,.42)}
.lf-sign svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.7}
/* La CTA de la demo (solo /demo/libro-de-firmas). Verde de marca y NO dorada a
   propósito: el dorado es del libro y la acción dorada es firmar. Esta es
   FastInvit hablando por encima del libro, igual que la barra de /demo/**, y
   tiene que distinguirse de un vistazo de la acción que el libro propone.
   Sin `text-transform` ni el tracking de la barra: es una frase, no una etiqueta. */
.lf-demo-cta{
  display:inline-flex;align-items:center;justify-content:center;text-align:center;
  min-height:44px;padding:12px 22px;border-radius:100px;
  background:var(--lf-primary);color:#fff;text-decoration:none;
  font-size:12px;font-weight:700;letter-spacing:.02em;text-transform:none;
  box-shadow:0 10px 26px rgba(6,10,8,.45);transition:transform .2s,box-shadow .2s;
}
.lf-demo-cta:hover{transform:translateY(-2px);box-shadow:0 14px 32px rgba(6,10,8,.55)}
/* Las dos de la demo en fila: crear la mía y, al lado, ver la demo de la invitación. La
   segunda en blanco: la principal sigue siendo crear. */
.lf-demo-ctas{display:flex;flex-wrap:wrap;justify-content:center;gap:10px}
.lf-demo-cta--alt{background:#fff;color:var(--lf-primary)}

/* ══════ Modal · Polaroid ══════ */
.lf-modal{
  position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center;padding:20px;
  overflow-y:auto;overscroll-behavior:contain;
  background:rgba(9,14,11,.72);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  opacity:0;pointer-events:none;transition:opacity .4s ease;
}
.lf-modal.is-on{opacity:1;pointer-events:auto}
.lf-sheet{
  width:min(360px,100%);display:flex;flex-direction:column;gap:16px;align-items:center;
  margin:auto;
  transform:translateY(22px) scale(.97);transition:transform .5s var(--lf-e);
}
.lf-modal.is-on .lf-sheet{transform:none}
/* La polaroid del modal va DERECHA, a diferencia de las de las hojas, que sí
   conservan su inclinación (ver .lf-pol y los slots con --r).
   NO es una preferencia estética: Chromium no pinta el cursor de un
   <textarea>/<input> que cuelga de un ancestro ROTADO. El campo sigue
   funcionando —recibe foco, acepta teclas, guarda el valor— pero sin cursor el
   formulario parece muerto. Forzar una capa de composición en el propio campo
   (translateZ) no sirve: el campo sigue dentro del sistema de coordenadas
   girado del padre. Quitar la rotación es lo único que lo devuelve.
   Enderezarla solo al enfocar (:focus-within) tampoco vale: el autofoco llega a
   los 480 ms y se ve el salto al abrir.
   De paso encaja con el gesto real: una tarjeta se endereza para escribir en
   ella. La entrada del modal —fade + subida de .lf-sheet— no se toca. */
.lf-shot-pol{
  width:min(100%,max(230px,calc((100dvh - 190px) / 1.82)));
  background:#fffdf7;padding:14px 14px 0;
  box-shadow:0 26px 60px rgba(6,10,8,.55);
}
.lf-shot{position:relative;aspect-ratio:3/4;overflow:hidden;background:#20281f;cursor:pointer}
.lf-shot.is-wide{aspect-ratio:4/3}
.lf-shot video,.lf-shot img{width:100%;height:100%;object-fit:cover;display:block}
/* El espejo es SOLO de la cámara de delante: uno se ve a sí mismo como en un espejo y
   cualquier otra cosa desconcierta. Con la trasera se quita, o el cartel de la mesa —o la
   tarta con el nombre— saldría escrito del revés. Lo pone y lo quita guestbook.js. */
.lf-shot video.is-espejo{transform:scaleX(-1)}
.lf-shot-off{
  position:absolute;inset:0;display:grid;place-content:center;gap:12px;justify-items:center;
  text-align:center;padding:26px;background:linear-gradient(150deg,#e6ddc9,#cfc3a8);
}
.lf-shot-off p{margin:0;font-family:'Patrick Hand',cursive;font-size:16px;color:#7d7359;line-height:1.4}
.lf-shot-off svg{width:30px;height:30px;fill:none;stroke:#a89a7c;stroke-width:1.4}
.lf-shot-flash{position:absolute;inset:0;background:#fff;opacity:0;pointer-events:none}
.lf-shot-flash.go{animation:lf-flash .5s ease}
@keyframes lf-flash{0%{opacity:.92}100%{opacity:0}}
.lf-cap{padding:14px 12px 18px;display:flex;flex-direction:column;gap:6px}
.lf-cap textarea,.lf-cap input{
  border:0;background:transparent;font-family:'Patrick Hand',cursive;color:#3e4a41;
  width:100%;padding:0;resize:none;outline:none;
}
.lf-cap textarea{font-size:18px;line-height:1.34;min-height:56px}
.lf-cap input{font-size:14.5px;color:#8b7f66;border-top:1px solid #e8e0cd;padding-top:8px}
.lf-cap textarea::placeholder,.lf-cap input::placeholder{color:#c0b79f}
.lf-acts{display:flex;align-items:center;gap:12px;width:100%}
.lf-btn{
  flex:1;min-height:46px;border:1px solid rgba(240,232,214,.3);border-radius:100px;
  background:transparent;color:#f0e8d6;font-size:11.5px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;transition:background .25s,border-color .25s;
}
.lf-btn:hover{background:rgba(240,232,214,.1)}
.lf-btn--go{background:var(--lf-gold);border-color:var(--lf-gold);color:#211a0c;font-weight:600}
.lf-btn--go:hover{background:#d5b271}
.lf-btn[disabled]{opacity:.4;pointer-events:none}
/* Botón del fogonazo. Arriba a la IZQUIERDA: a la derecha vive "Otra vez", y los dos
   estados de la foto no coinciden nunca —uno se ve en directo y el otro con la foto ya
   hecha—, pero ponerlos en el mismo sitio haría que uno saltara sobre el otro. */
/* ── Los mandos de la cámara ──
   Tres celdas fijas con el obturador en la del medio: los laterales aparecen y
   desaparecen (el flash cuando no puede alumbrar, el rotar cuando no hay dos cámaras) y
   el del centro no se mueve nunca. Por eso las columnas van declaradas y cada botón
   colocado a mano; con colocación automática, esconder el flash correría el obturador. */
.lf-cam-bar{
  position:absolute;inset:auto 0 0;z-index:3;display:none;
  grid-template-columns:1fr auto 1fr;align-items:center;
  padding:16px 14px 12px;
  background:linear-gradient(transparent,rgba(10,16,12,.62));
}
.lf-cam-side{
  grid-row:1;justify-self:center;display:none;place-items:center;
  width:42px;height:42px;border:0;border-radius:50%;
  background:rgba(10,16,12,.42);color:#f3ecdb;backdrop-filter:blur(6px);
  position:relative;padding:0;
}
.lf-cam-side svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.lf-flashbtn{grid-column:1}
.lf-flip{grid-column:3}
/* El rayo va relleno y no de trazo: a 19px un contorno se emborrona. */
.lf-ic-bolt{fill:currentColor;stroke:none}
.lf-flashbtn.is-on{background:rgba(232,196,106,.92);color:#2b2415}
/* La "A" de automático, en la esquina del rayo. Es la convención de las apps de cámara
   y no hay que traducirla; el texto de verdad va en el aria-label. */
.lf-flash-a{
  position:absolute;right:7px;bottom:6px;font-size:9px;font-weight:800;line-height:1;
  display:none;
}
.lf-flashbtn.is-auto .lf-flash-a{display:block}
/* Tachado del flash apagado: una barra en diagonal sobre el rayo. */
.lf-flash-tacha{
  position:absolute;left:50%;top:50%;width:26px;height:1.6px;border-radius:2px;
  background:currentColor;transform:translate(-50%,-50%) rotate(-45deg);display:none;
}
.lf-flashbtn.is-off .lf-flash-tacha{display:block}
.lf-flashbtn.is-off .lf-ic-bolt{opacity:.55}

/* El obturador: aro y disco, como el de cualquier cámara. Blanco y no dorado a propósito
   —el dorado es del libro, y aquí lo que hace falta es que se lea "esto dispara" sin
   pensarlo—; el dorado se reserva para el flash encendido. */
.lf-shutter{
  grid-column:2;grid-row:1;display:grid;place-items:center;
  width:62px;height:62px;padding:0;border-radius:50%;
  border:3px solid rgba(255,255,255,.92);background:transparent;
}
.lf-shutter-in{
  width:46px;height:46px;border-radius:50%;background:#fff;
  transition:transform .12s ease,background .12s ease;
}
.lf-shutter:active .lf-shutter-in{transform:scale(.86);background:#e9e2d2}
.lf-shutter-redo{display:none;width:26px;height:26px;fill:none;stroke:#fff;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
/* Ya hay foto: el mismo botón, en el mismo sitio, ahora vuelve a tomarla. */
.lf-shutter.is-redo{background:rgba(10,16,12,.42);backdrop-filter:blur(6px)}
.lf-shutter.is-redo .lf-shutter-in{display:none}
.lf-shutter.is-redo .lf-shutter-redo{display:block}

/* El velo del fogonazo. z-index por encima de todo —el modal está a 50— porque su
   trabajo es iluminar, y cualquier cosa por delante le come luz.

   ENTRA de golpe y SALE despacio, y por eso la transición vive en la regla base y se
   anula en .is-on: al añadir la clase manda `transition:none` y el blanco aparece en el
   mismo fotograma —que es lo que ilumina—, y al quitarla vuelve a valer la de aquí y se
   funde. Con una sola transición para los dos sentidos, el flash tardaría medio segundo
   en encenderse y no alumbraría nada.
   La visibilidad se retrasa lo que dura el fundido, o desaparecería de golpe al empezar. */
.lf-fogonazo{
  position:fixed;inset:0;z-index:9999;background:#fff;pointer-events:none;
  opacity:0;visibility:hidden;
  transition:opacity .5s ease, visibility 0s linear .5s;
}
.lf-fogonazo.is-on{opacity:1;visibility:visible;transition:none}

/* El revelado de la polaroid. La foto asoma quemada y va bajando a su exposición real,
   como una polaroid de verdad al revelarse. Va sobre la imagen y no sobre otro velo gris
   porque así lo que se aclara es la FOTO: el gris encima solo la taparía. */
/* El orden es contrast() ANTES que brightness(), y no al reves, porque es lo unico
   que blanquea de verdad. brightness() MULTIPLICA: una sombra de valor 20 por 6 sigue
   siendo 120, gris, y la foto se seguia viendo nada mas dispararla. contrast(0) lleva
   todo a gris medio primero, y ahi si el multiplicador lo empuja a blanco puro —medido:
   con la formula de antes una sombra se quedaba en 123, con esta llega a 255.
   Las sombras son las primeras en asomar, que es como se revela una polaroid.
   Todos los fotogramas llevan las MISMAS funciones y en el mismo orden: si una lista
   cambia entre fotogramas, el navegador no puede interpolarla y la animacion salta. */
@keyframes lf-revelar{
  0%   { filter:contrast(0)    brightness(2.4)  saturate(0);   }
  14%  { filter:contrast(.22)  brightness(2.2)  saturate(.2);  }
  32%  { filter:contrast(.5)   brightness(1.8)  saturate(.5);  }
  58%  { filter:contrast(.8)   brightness(1.35) saturate(.8);  }
  100% { filter:contrast(1)    brightness(1)    saturate(1);   }
}
/* 2.5s: cae rapido al principio y se asienta despacio, como una polaroid de verdad.
   Los porcentajes se recalibraron al alargarla — con los de 1.4s repartidos sobre el
   doble de tiempo, la foto se quedaba casi un segundo quemada y parecia colgada. */
#lf-shot-img.is-revelando{ animation:lf-revelar 2.5s cubic-bezier(.2,.7,.3,1) both; }

/* Aviso de vista previa del dueño. Arriba y discreto: informa sin competir con
   el libro, que es lo que ha venido a mirar. */
.lf-owner-note{
  position:fixed;top:0;left:0;right:0;z-index:70;text-align:center;
  padding:9px 16px;font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;
  color:#3a2f14;background:rgba(197,160,89,.92);
}
.lf-toast{
  position:fixed;left:50%;bottom:96px;transform:translate(-50%,14px);z-index:60;
  background:rgba(18,26,20,.9);color:#f0e8d6;font-size:11.5px;letter-spacing:.14em;
  padding:12px 20px;border-radius:100px;opacity:0;transition:opacity .3s,transform .3s;
  border:1px solid rgba(197,160,89,.34);pointer-events:none;
}
.lf-toast.is-on{opacity:1;transform:translate(-50%,0)}

/* ══════ Móvil EN RETRATO: la misma mecánica que en PC ══════
   Solo en retrato: girado el teléfono el libro entra entero y manda el bloque de
   apaisado que hay justo debajo.
   No es otro libro: es el mismo spread de dos hojas, más ancho que la pantalla
   y corrido a la izquierda. Se ve la hoja derecha entera y un pedacito de la
   anterior — marrón en la primera vista, papel después. Al no haber un camino
   distinto, el giro de página es exactamente el de escritorio. */
@media (max-width:760px) and (orientation:portrait){
  /* El libro se ancla por su borde izquierdo, no por el centro del contenedor:
     el margen se calcula desde el ancho real del libro para que el pliegue caiga
     siempre en 12vw. Cerrado, un desplazamiento centra la tapa (la mitad
     derecha). Todo en vh/vw, sin depender de cómo se centre la grilla. */
  .lf-stage{padding:16px 0 0}
  .lf-bookwrap{--bw:min(172vw,calc(72vh * 1.44))}
  .lf-book{
    justify-self:start;
    margin-left:calc(12vw - var(--bw) / 2);
    --sx:calc(38vw - var(--bw) / 4);
  }
  .lf-book.is-open{transform:none}
  .lf-nav{width:40px;height:40px;background:rgba(18,26,20,.66)}
  .lf-nav--prev{left:14px}
  .lf-nav--next{right:10px}
  .lf-bar{gap:10px;font-size:10px}
}

/* ══════ Apaisado: el libro ENTERO, como en PC ══════
   Girado el teléfono se invierte el problema —sobra ancho y falta alto—, así que
   el truco de la media hoja deja de tener sentido: se veía media página pegada a
   un lado y la otra mitad de la pantalla vacía. Aquí cabe el spread completo, que
   además es el que el visitante reconoce de la landing.
   El cromo se comprime (barra en una fila) porque en apaisado la altura es el
   recurso escaso: todo lo que no gasta la barra se lo queda el libro, y de ahí
   sale una hoja MÁS grande que la que se veía antes a media pantalla.
   Se filtra por altura y no por ancho para coger también los móviles grandes, que
   girados pasan de 760px de ancho y hasta ahora caían en el layout de escritorio
   con el libro desbordando la pantalla por abajo.
   OJO: guestbook.js decide con esta MISMA media query cuántas firmas entran por
   hoja (tres y una hoja por vista en móvil, cuatro y dos en escritorio). Si cambia
   una, hay que cambiar la otra o el reparto y el dibujo dejan de coincidir. */
@media (orientation:landscape) and (max-height:520px){
  .lf-stage{padding:8px 0 0}
  /* 74vh y no más: por debajo de la barra (44 de botón + su aire) hay que dejar sitio
     de verdad, y con 80vh el libro se le montaba encima en los teléfonos más bajos
     (320 px de alto girados). El ancho lo pone la proporción, así que subirlo aquí es
     ganar hoja a costa de comerse los botones. */
  .lf-bookwrap{--bw:min(94vw,calc(74vh * 1.44))}
  .lf-nav{width:38px;height:38px;background:rgba(18,26,20,.66)}
  .lf-nav--prev{left:max(8px,calc(50% - var(--bw) / 2 - 46px))}
  .lf-nav--next{right:max(8px,calc(50% - var(--bw) / 2 - 46px))}
  .lf-bar{
    flex-direction:row;flex-wrap:wrap;justify-content:center;
    gap:12px;padding:8px 0 10px;font-size:10px;
  }
  .lf-sign{padding:11px 18px}
  .lf-demo-cta{min-height:40px;padding:10px 16px;font-size:11px}
}
@media (prefers-reduced-motion:reduce){
  .lf-book,.lf-cover,.lf-spread,.lf-flip,.lf-pol{transition-duration:.01ms!important;animation-duration:.01ms!important}
  .lf-book:not(.is-open):not(.is-closing),.lf-cover-hint,.lf-book.is-nudge .lf-cover-hint{animation:none}
}
