/* ═══════════════════════════════════════════════════════════════════════
   Seafoam & Sunset · Formal Invitation (card) — Beach Tropical Wedding
   Airy stationery · seafoam, blush & sand · golden-hour light

   Structure:
     1. Fonts & design tokens
     2. Base / reset
     3. App shell (single-viewport, one active panel)
     4. Panel rail (generated by main.js)
     5. Cover scene (sun, palms, waves, grain, optional photo bg)
     6. Cover content (monogram, letter, photo chip)
     7. Details panel (events, good-to-know)
     7b. Our Story gallery (polaroid grid) + lightbox
     8. Countdown
     9. RSVP form
    10. Guestbook injected markup
    11. RSVP injected states (thank-you / closed)
    12. Personalisation
    13. Reduced motion
    14. Print
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1. Fonts & tokens ──────────────────────────────────────────────── */
@font-face {
  font-family: 'Butler';
  src: url('../fonts/Butler_Light.otf') format('opentype');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Butler';
  src: url('../fonts/Butler_Regular.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Astagina';
  src: url('../fonts/AstaginaSignature.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('../fonts/montserrat-300.woff2') format('woff2'),
       url('../fonts/montserrat-300.woff') format('woff'),
       url('../fonts/montserrat-300.ttf') format('truetype');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('../fonts/montserrat-400.woff2') format('woff2'),
       url('../fonts/montserrat-400.woff') format('woff'),
       url('../fonts/montserrat-400.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('../fonts/montserrat-500.woff2') format('woff2'),
       url('../fonts/montserrat-500.woff') format('woff'),
       url('../fonts/montserrat-500.ttf') format('truetype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

:root {
  /* pastel palette — seafoam & sunset stationery */
  --sun: #F5B459;
  --sun-soft: #FBD9A3;
  --coral: #EF8F74;
  --coral-soft: #F7C6B6;
  --blush: #FBE9E1;
  --blush-deep: #F5CFC4;
  --sand: #FBF3E6;
  --paper: #FFFBF2;        /* invitation paper */
  --seafoam: #BFE2D2;
  --aqua: #86C8BC;
  --aqua-deep: #5FA89C;
  --ink: #28534F;
  --ink-soft: #5E8580;
  --ink-faint: #93AAA6;
  --white: #FFFDF9;
  --line: rgba(40, 83, 79, 0.14);
  --shadow-soft: 0 18px 44px -20px rgba(40, 83, 79, 0.30);
  --shadow-card: 0 24px 60px -28px rgba(40, 83, 79, 0.42);

  --font-script: 'Astagina', 'Snell Roundhand', 'Brush Script MT', cursive;
  --font-serif: 'Butler', 'Didot', 'Playfair Display', Georgia, 'Times New Roman', serif;
  --font-sans: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --radius: 20px;
  --gutter: clamp(20px, 5vw, 48px);
}

/* ── 2. Base ────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 300;
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
}
h1, h2, h3, p, figure, ul { margin: 0; }
ul { padding: 0; list-style: none; }
img { max-width: 100%; display: block; }
button { font: inherit; }
a { color: var(--aqua-deep); }

[hidden] { display: none !important; }

::selection { background: var(--sun-soft); color: var(--ink); }

:focus-visible {
  outline: 2px solid var(--aqua-deep);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ── 3. App shell ─────────────────────────────────────────────────────
   With .js: one fixed viewport; only the active panel is visible and
   scrolls internally (the shell engages only once main.js adds
   html.app-ready — if JS never runs, panels stack and the page scrolls — content is never locked away. */
.app { min-height: 100vh; min-height: 100dvh; }

.app-ready .app {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

.panel-stack { flex: 1 1 auto; }
.app-ready .panel-stack { position: relative; overflow: hidden; }

.panel {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
}

.app-ready .panel {
  position: absolute;
  inset: 0;
  min-height: 0;
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px);
  transition:
    opacity 0.45s ease,
    transform 0.45s ease,
    visibility 0s linear 0.45s;
}
.app-ready .panel.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity 0.45s ease,
    transform 0.45s ease,
    visibility 0s linear 0s;
  z-index: 1;
}

.app-ready .panel { padding-bottom: env(safe-area-inset-bottom); }

/* the cover panel: scene fixed, content scrolls over it */
.app-ready .panel-cover { overflow: hidden; padding-bottom: 0; }
.app-ready .panel-cover .cover {
  position: relative;
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

/* ── 4. Panel rail ──────────────────────────────────────────────────── */
.panel-nav {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: rgba(255, 251, 242, 0.85);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  position: relative;
  z-index: 30;
}

/* Inner scroller (built by main.js). On phones the rail stays ONE row
   and slides sideways instead of wrapping into two or three rows that
   would eat the invitation's fixed viewport. */
.panel-nav-track {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: 6px;
  max-width: 100%;
  min-width: 0;
}

/* prev/next steppers — phones only (see 4b); built by main.js */
.pn-arrow { display: none; }
.panel-nav-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 10px 13px;
  min-height: 40px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  touch-action: manipulation;
  transition: color 0.25s ease, background 0.25s ease;
}
.panel-nav-btn .pn-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.45;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
.panel-nav-btn:hover { color: var(--ink); background: rgba(40, 83, 79, 0.06); }
.panel-nav-btn.is-active {
  color: var(--white);
  background: linear-gradient(135deg, var(--coral), var(--sun));
  box-shadow: 0 8px 18px -8px rgba(239, 143, 116, 0.8);
}
.panel-nav-btn.is-active .pn-dot { opacity: 1; transform: scale(1.35); }

/* ── 4b. Rail on phones ───────────────────────────────────────────────
   Small screens: ONE inline row — prev/next steppers flank the
   swipeable tabs. Edge fades hint at more tabs, scroll-snap settles a
   tab under the thumb, and main.js keeps the active tab centred and
   greys a stepper out at either end of the invitation. */
@media (max-width: 700px) {
  .panel-nav {
    align-items: center;
    gap: 6px;
    padding: 7px 10px calc(7px + env(safe-area-inset-bottom));
  }

  /* prev / next — one tap along the invitation */
  .pn-arrow {
    display: inline-flex;
    flex: 0 0 auto;
    appearance: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: rgba(255, 251, 242, 0.65);
    color: var(--ink);
    cursor: pointer;
    touch-action: manipulation;
    transition: opacity 0.25s ease, background 0.25s ease, transform 0.15s ease;
  }
  .pn-arrow svg { width: 16px; height: 16px; display: block; }
  .pn-arrow:hover { background: rgba(40, 83, 79, 0.08); }
  .pn-arrow:active { transform: scale(0.94); }
  .pn-arrow[disabled] { opacity: 0.32; cursor: default; pointer-events: none; }

  /* fade hints live on the nav so they never scroll away — tucked
     beside the steppers, not over them */
  .panel-nav::before,
  .panel-nav::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 20px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.3s ease;
    z-index: 2;
  }
  .panel-nav::before {
    left: 62px;
    background: linear-gradient(90deg, rgba(255, 251, 242, 0.97) 25%, rgba(255, 251, 242, 0));
  }
  .panel-nav::after {
    right: 62px;
    background: linear-gradient(-90deg, rgba(255, 251, 242, 0.97) 25%, rgba(255, 251, 242, 0));
  }
  .panel-nav.is-off-left::before,
  .panel-nav.is-off-right::after { opacity: 1; }

  /* the tab strip squeezes between the two steppers */
  .panel-nav-track {
    flex: 1 1 auto;
    min-width: 0;
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;   /* swiping the rail never scrolls the panel */
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    scroll-padding-inline: 14px;
    padding: 2px 14px;
    gap: 4px;
  }
  .panel-nav-track::-webkit-scrollbar { display: none; }

  .panel-nav-btn {
    flex: 0 0 auto;
    scroll-snap-align: center;
    font-size: 0.66rem;
    letter-spacing: 0.1em;
    padding: 8px 11px;
    min-height: 42px;
  }
}

/* roomier touch targets on real fingers */
@media (pointer: coarse) {
  .panel-nav-btn { min-height: 44px; }
}

/* very narrow phones — tighten the pills a touch more */
@media (max-width: 360px) {
  .panel-nav { padding-inline: 8px; }
  .panel-nav-track { padding-inline: 10px; scroll-padding-inline: 10px; }
  .panel-nav::before { left: 58px; }
  .panel-nav::after { right: 58px; }
  .panel-nav-btn { padding: 8px 9px; letter-spacing: 0.08em; }
}

/* ── 5. Cover scene ─────────────────────────────────────────────────── */
.panel-cover {
  background:
    radial-gradient(120% 90% at 50% 108%, var(--blush) 0%, var(--blush-deep) 34%, transparent 70%),
    linear-gradient(180deg, #EDF6F0 0%, var(--seafoam) 42%, var(--blush) 100%);
}

.scene {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.sun {
  position: absolute;
  left: 50%;
  top: 13%;
  width: min(44vw, 210px);
  aspect-ratio: 1;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 50% 46%, #FFF7E6 0%, #FBE3AC 52%, #F6BE72 100%);
  box-shadow:
    0 0 40px 12px rgba(246, 190, 114, 0.32),
    0 0 110px 60px rgba(251, 219, 163, 0.25);
}

.birds {
  position: absolute;
  top: 7%;
  right: 12%;
  width: 110px;
  height: 28px;
  opacity: 0.9;
}

.palm { position: absolute; color: rgba(40, 83, 79, 0.14); }
.palm--left {
  top: -6%;
  left: -14%;
  width: min(50vw, 290px);
  height: auto;
  color: rgba(40, 83, 79, 0.18);
}
.palm--right {
  bottom: -2%;
  right: -16%;
  width: min(44vw, 250px);
  height: auto;
  transform: scaleX(-1);
  color: rgba(40, 83, 79, 0.11);
}

.waves {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: clamp(90px, 18dvh, 170px);
  pointer-events: none;
}
.wave { position: absolute; left: 0; width: 100%; height: 100%; }
.wave--back { top: 0;  color: var(--blush-deep); opacity: 0.85; }
.wave--mid  { top: 10%; color: var(--seafoam);    opacity: 0.8; }
.wave--front{ top: 22%; color: var(--aqua);       opacity: 0.92; }
.wave path { fill: currentColor; }

@media (prefers-reduced-motion: no-preference) {
  .wave--back { animation: drift 26s ease-in-out infinite alternate; }
  .wave--mid  { animation: drift 22s ease-in-out infinite alternate-reverse; }
  .wave--front{ animation: drift 18s ease-in-out infinite alternate; }
}
@keyframes drift {
  from { transform: translateX(0) scaleX(1.01); }
  to   { transform: translateX(-1.4%) scaleX(1.03); }
}

.grain {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: 0.5;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url(%23n)' opacity='0.05'/></svg>");
}

/* optional photo background — host opt-in, off by default. To use it:
   add class="has-photo-bg" to .panel-cover and put an
   <img class="cover-photo" ...> as the first child of .scene (a
   commented example sits in index.html). The drawn sun steps aside so
   the photo reads; palms, waves and grain stay on top for the
   illustration feel. Delete the img / drop the class to remove it. */
.cover-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.panel-cover.has-photo-bg .cover-photo { opacity: 0.8; }
.panel-cover.has-photo-bg .sun { display: none; }

/* ── 6. Cover content ───────────────────────────────────────────────── */
.cover {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: clamp(28px, 6dvh, 56px) var(--gutter) clamp(40px, 9dvh, 88px);
  max-width: 560px;
  margin: 0 auto;
  min-height: 100%;
}

.monogram {
  width: clamp(74px, 20vw, 104px);
  aspect-ratio: 1;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(239, 143, 116, 0.5);
  box-shadow: inset 0 0 0 5px rgba(255, 251, 242, 0.55), inset 0 0 0 6px rgba(95, 168, 156, 0.28);
  margin-bottom: clamp(16px, 3.4dvh, 30px);
}
.monogram-inner {
  font-family: var(--font-script);
  font-size: clamp(1.6rem, 7vw, 2.4rem);
  color: var(--coral);
  line-height: 1;
}

.cover-head { margin-bottom: clamp(14px, 3dvh, 26px); }

.eyebrow {
  font-size: clamp(0.66rem, 2vw, 0.78rem);
  font-weight: 500;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.couple-names {
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(3rem, 12vw, 5.6rem);
  line-height: 1.12;
  color: var(--ink);
  margin-top: 6px;
  text-shadow: 0 10px 30px rgba(40, 83, 79, 0.10);
}
.couple-names .amp {
  display: inline-block;
  font-size: 0.5em;
  color: var(--coral);
  vertical-align: 0.3em;
  margin: 0 0.04em;
}

.tagline {
  font-family: var(--font-serif);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(0.98rem, 3.4vw, 1.3rem);
  color: var(--ink-soft);
  max-width: 34ch;
  margin: 10px auto 0;
}

/* the letter — reads like a printed stationery card */
.cover-letter {
  margin-top: clamp(16px, 4dvh, 30px);
  padding: clamp(20px, 4.5vw, 30px) clamp(20px, 5vw, 34px);
  background: rgba(255, 251, 242, 0.72);
  border: 1px solid rgba(255, 251, 242, 0.95);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.letter-greeting {
  font-family: var(--font-serif);
  font-size: clamp(1.05rem, 3.4vw, 1.3rem);
  color: var(--ink);
  margin-bottom: 8px;
}
.letter-greeting .guest-greeting { font-style: italic; color: var(--coral); }
.letter-body {
  font-size: clamp(0.92rem, 3vw, 1.02rem);
  color: var(--ink-soft);
}
.letter-body strong { color: var(--ink); font-weight: 400; }

.cover-meta { margin-top: clamp(16px, 3.6dvh, 30px); }
.date-line {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(1.3rem, 5vw, 1.9rem);
  color: var(--ink);
}
.place-line {
  margin-top: 4px;
  font-size: clamp(0.7rem, 2.4vw, 0.84rem);
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--aqua-deep);
}

/* photo chip (polaroid) */
.photo-chip {
  position: relative;
  margin: clamp(16px, 3.6dvh, 28px) auto 0;
  width: min(66vw, 236px);
  padding: 10px 10px 8px;
  background: var(--white);
  border-radius: 8px;
  box-shadow: var(--shadow-card);
  transform: rotate(-1.5deg);
}
.photo-chip-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 5px;
}
.chip-open {
  appearance: none;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: zoom-in;
  border-radius: 5px;
}
.photo-chip-tape {
  position: absolute;
  top: -11px;
  left: 50%;
  width: 74px;
  height: 24px;
  transform: translateX(-50%) rotate(-3deg);
  background: rgba(251, 217, 163, 0.75);
  box-shadow: 0 2px 6px rgba(40, 83, 79, 0.12);
}
.photo-chip-note {
  margin-top: 8px;
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}

.cover-foot { margin-top: clamp(14px, 3dvh, 24px); }
.follow {
  font-size: clamp(0.86rem, 2.8vw, 0.98rem);
  font-style: italic;
  font-family: var(--font-serif);
  color: var(--ink-soft);
}
.hashtag {
  margin-top: 8px;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--coral);
}

/* ── 7. Details panel ───────────────────────────────────────────────── */
.panel-details,
.rsvp-wrap,
.guestbook-section,
.gallery-section,
.panel-free {
  background:
    radial-gradient(140% 60% at 50% -10%, #FDF1E2 0%, rgba(253, 241, 226, 0) 62%),
    linear-gradient(180deg, var(--paper) 0%, #F6F0E2 100%);
}

.panel-scroll {
  max-width: 620px;
  margin: 0 auto;
  padding: clamp(30px, 7dvh, 60px) var(--gutter) calc(60px + env(safe-area-inset-bottom));
}

.section-head, .gallery-head { text-align: center; margin-bottom: 26px; }
.section-title, .rsvp-title, .guestbook-title, .gallery-title {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(1.8rem, 7vw, 2.5rem);
  color: var(--ink);
  margin-top: 10px;
}
.section-description, .rsvp-description, .guestbook-description, .gallery-description {
  margin: 12px auto 0;
  font-size: clamp(0.9rem, 3vw, 1rem);
  color: var(--ink-soft);
  max-width: 46ch;
}

/* repeatable event cards — one event or many, grid adapts */
.events {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px;
  margin-top: 6px;
}
.event {
  background: rgba(255, 251, 242, 0.8);
  border: 1px solid rgba(95, 168, 156, 0.22);
  border-radius: var(--radius);
  padding: 24px 22px;
  text-align: center;
  box-shadow: var(--shadow-soft);
  position: relative;
}
.event::before { /* little coral finial above each card */
  content: "";
  position: absolute;
  top: -1px;
  left: 50%;
  transform: translateX(-50%);
  width: 64px;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--coral-soft), var(--sun-soft), var(--seafoam));
}
.event-icon {
  display: inline-block;
  color: var(--coral);
  font-size: 1rem;
  margin-bottom: 6px;
}
.event-label {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 1.35rem;
  color: var(--ink);
}
.event-time {
  margin-top: 6px;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--aqua-deep);
}
.event-place {
  margin-top: 10px;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 1.02rem;
  color: var(--ink);
}
.event-address {
  margin-top: 2px;
  font-size: 0.8rem;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
}
.event-note {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
  font-size: 0.82rem;
  color: var(--ink-soft);
}

/* Empty optional event lines hide instead of leaving a blank line in the
   card (the engine's hidden_if_empty also removes them at render; this is
   the CSS-level guard for the shipped file / any render path). The required
   .event-label is never hidden. */
.event-time:empty,
.event-place:empty,
.event-address:empty,
.event-note:empty {
  display: none;
}

.good-to-know {
  margin-top: 26px;
  background: rgba(191, 226, 210, 0.35);
  border: 1px solid rgba(95, 168, 156, 0.2);
  border-radius: var(--radius);
  padding: 22px 24px;
}
.gtk-title {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 1.15rem;
  color: var(--ink);
  margin-bottom: 10px;
}
.gtk-list li {
  position: relative;
  padding-left: 22px;
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin: 7px 0;
}
.gtk-list li::before {
  content: "✦";
  position: absolute;
  left: 2px;
  top: 0;
  color: var(--coral);
  font-size: 0.72rem;
}
.gtk-hashtag { color: var(--aqua-deep); font-weight: 500; letter-spacing: 0.05em; }

/* ── 7b. Our Story gallery (photos) ───────────────────────────────────
   Optional photo feature. Delete the whole .gallery-section panel to
   remove it (the rail rebuilds itself); each .gallery-item is a
   repeatable group — clone one child per photo. Every image ships with
   an inline-SVG placeholder shown by default, plus a data-placeholder
   fallback, so the grid never shows an empty or broken frame. */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: clamp(16px, 3vw, 22px);
  margin-top: 6px;
}
.gallery-item {
  position: relative;
  background: var(--white);
  border-radius: 8px;
  padding: 9px 9px 8px;
  box-shadow: var(--shadow-card);
  transition: transform 0.35s ease, box-shadow 0.35s ease;
}
.gallery-item:nth-child(3n+1) { transform: rotate(-1.6deg); }
.gallery-item:nth-child(3n+2) { transform: rotate(1.2deg); }
.gallery-item:nth-child(3n)   { transform: rotate(-0.9deg); }
.gallery-item:hover,
.gallery-item:focus-within {
  transform: rotate(0deg) translateY(-4px);
  box-shadow: 0 26px 54px -22px rgba(40, 83, 79, 0.5);
  z-index: 1;
}
.gallery-open {
  appearance: none;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: zoom-in;
  border-radius: 5px;
}
.gallery-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 5px;
}
.gallery-caption {
  margin-top: 8px;
  text-align: center;
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* lightbox — full-view photo dialog (driven by main.js; [hidden]
   keeps it out of the page until a photo is opened) */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 40px);
  background: rgba(22, 44, 41, 0.72);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  cursor: zoom-out;
}
.lightbox-frame {
  margin: 0;
  max-width: min(720px, 100%);
  background: var(--white);
  border-radius: 10px;
  padding: 12px 12px 8px;
  box-shadow: 0 30px 80px -18px rgba(10, 26, 24, 0.65);
  transform: rotate(-1deg);
  cursor: default;
  animation: lightbox-in 0.28s ease;
}
.lightbox-img {
  max-width: 100%;
  max-height: 72vh;
  border-radius: 6px;
  margin: 0 auto;
}
.lightbox-caption {
  margin-top: 8px;
  text-align: center;
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lightbox-close {
  position: absolute;
  top: clamp(12px, 3vw, 20px);
  right: clamp(12px, 3vw, 20px);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 251, 242, 0.92);
  color: var(--ink);
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 24px -10px rgba(10, 26, 24, 0.55);
  transition: transform 0.2s ease, background 0.2s ease;
}
.lightbox-close:hover { transform: scale(1.1); background: var(--white); }
@keyframes lightbox-in {
  from { opacity: 0; transform: rotate(-1deg) scale(0.95); }
  to   { opacity: 1; transform: rotate(-1deg) scale(1); }
}

/* lightbox zoom — tap/click the photo to zoom in (frame scrolls to
   explore), tap again to zoom out. Keyboard: Enter/Space on the photo. */
.lightbox-img { cursor: zoom-in; }
.lightbox-frame.is-zoomed {
  overflow: auto;
  max-width: 94vw;
  max-height: 94vh;
  transform: none;
  animation: none;
  cursor: default;
  -webkit-overflow-scrolling: touch;
}
.lightbox-frame.is-zoomed .lightbox-img {
  max-width: none;
  max-height: none;
  width: auto;
  height: auto;
  cursor: zoom-out;
}
.lightbox-hint {
  margin-top: 8px;
  text-align: center;
  font-size: 0.56rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ── 7c. Official flyer / banner (optional) ────────────────────────────
   A slot for hosts who already have a designed flyer or save-the-date
   poster — display it so guests can confirm they are in the right
   place. Delete the whole <figure class="banner-flyer"> block to hide
   the feature. Clicking it opens the full-screen viewer (main.js). */
.banner-flyer {
  margin: clamp(26px, 5vw, 40px) auto 0;
  max-width: 680px;
  text-align: center;
}
.banner-open {
  appearance: none;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: zoom-in;
  border-radius: 10px;
}
.banner-img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 8 / 3;
  object-fit: cover;
  padding: 6px;
  background: var(--white);
  border: 1px solid rgba(95, 168, 156, 0.25);
  border-radius: 10px;
  box-shadow: var(--shadow-card);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.banner-flyer:hover .banner-img,
.banner-flyer:focus-within .banner-img {
  transform: translateY(-3px);
  box-shadow: 0 28px 60px -24px rgba(40, 83, 79, 0.5);
}
.banner-caption {
  margin-top: 10px;
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ── 7d. Optional film feature (video URL embed) ──────────────────────
   Ships visible: the card always shows its poster, with a gentle note
   until data-video-url holds a URL (YouTube / Vimeo / any embeddable
   link). Guests tap play and an <iframe> is lazily built — nothing
   loads until then. Empty URL = poster + note (tap opens the photo
   viewer); data-video-url="off" or deleting the block hides it. */
.film-card {
  margin-top: clamp(28px, 5vw, 40px);
  text-align: center;
}
.film-label {
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.film-frame {
  position: relative;
  width: 100%;
  max-width: 640px;
  margin: 14px auto 0;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 12px;
  background: rgba(40, 83, 79, 0.08);
  border: 1px solid rgba(95, 168, 156, 0.3);
  box-shadow: var(--shadow-card);
}
.film-open {
  appearance: none;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.film-poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.film-play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 66px;
  height: 66px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
  background: linear-gradient(135deg, var(--coral), var(--sun));
  box-shadow: 0 12px 26px -10px rgba(239, 143, 116, 0.9);
  animation: film-pulse 2.6s ease-in-out infinite;
  transition: transform 0.25s ease;
}
.film-play svg { width: 26px; height: 26px; margin-left: 3px; }
.film-open:hover .film-play { transform: translate(-50%, -50%) scale(1.08); }
.film-hint {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--white);
  background: rgba(40, 83, 79, 0.55);
  padding: 6px 12px;
  border-radius: 999px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.film-note {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  max-width: calc(100% - 32px);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--white);
  background: rgba(40, 83, 79, 0.55);
  padding: 7px 14px;
  border-radius: 999px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.film-frame.is-playing iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.film-external {
  display: inline-block;
  margin-top: 14px;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--aqua-deep);
  padding: 10px 18px;
  border: 1px solid rgba(95, 168, 156, 0.4);
  border-radius: 999px;
  text-decoration: none;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.film-external:hover { background: rgba(95, 168, 156, 0.1); border-color: var(--aqua-deep); }
.film-external[hidden] { display: none; }
@keyframes film-pulse {
  0%, 100% { box-shadow: 0 12px 26px -10px rgba(239, 143, 116, 0.9), 0 0 0 0 rgba(239, 143, 116, 0.45); }
  50%      { box-shadow: 0 12px 26px -10px rgba(239, 143, 116, 0.9), 0 0 0 14px rgba(239, 143, 116, 0); }
}

/* ── 7e. Free page (optional, host-defined) ──────────────────────────
   A free-text page of repeatable blocks: each block has a free title,
   free text and an OPTIONAL photo (a block renders as title + text
   without its photo). Leave the whole group empty — or toggle the page
   off in the cockpit — and the platform removes the whole #panel-free
   section; the rail rebuilds itself, so the tab and its dot disappear
   with it. Move the <section class="panel panel-free"> block in
   index.html to re-position the page among the tabs (the rail follows
   the DOM order). Every free image is clickable and opens the
   full-screen viewer with zoom (main.js), like every other photo. */
.free-page { margin-top: 2px; }
.free-blocks {
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.free-block {
  background: rgba(255, 251, 242, 0.8);
  border: 1px solid rgba(95, 168, 156, 0.22);
  border-radius: var(--radius);
  padding: clamp(20px, 4.5vw, 30px);
  box-shadow: var(--shadow-soft);
}
.free-title {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(1.2rem, 4.4vw, 1.55rem);
  color: var(--ink);
  margin-bottom: 10px;
}
.free-content {
  font-size: clamp(0.92rem, 3vw, 1rem);
  line-height: 1.7;
  color: var(--ink-soft);
}
.free-content p { margin: 0; }
.free-figure {
  margin: 16px 0 0;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgba(95, 168, 156, 0.25);
  background: var(--white);
  box-shadow: var(--shadow-soft);
}
.free-open {
  appearance: none;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: zoom-in;
  border-radius: 12px;
}
.free-img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.free-cap {
  padding: 10px 14px;
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}

/* ── 8. Countdown ─────────────────────────────────────────────────────
   Hidden by default; main.js adds .is-live once the platform has filled
   the cells with a real deadline (no flash of zeros). */
.countdown-wrap {
  margin: 0 auto clamp(18px, 4dvh, 28px);
  max-width: 460px;
  display: none;
}
.countdown-wrap.is-live { display: block; }
.countdown-label {
  text-align: center;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 12px;
}
.countdown {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: clamp(10px, 3vw, 18px);
  padding: 12px 20px;
  border-radius: 999px;
  background: rgba(255, 251, 242, 0.75);
  border: 1px solid rgba(255, 251, 242, 0.95);
  box-shadow: var(--shadow-soft);
}
.count-cell { display: flex; flex-direction: column; align-items: center; min-width: 2.4ch; }
.count-cell > span:first-child {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(1.4rem, 6vw, 1.9rem);
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.count-caption {
  margin-top: 4px;
  font-size: 0.56rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.count-sep { color: var(--coral); font-weight: 500; }

/* ── 9. RSVP form ───────────────────────────────────────────────────── */
.rsvp-head, .guestbook-head { text-align: center; margin-bottom: 24px; }

.guest-greeting {
  margin-top: 14px;
  display: inline-block;
  padding: 8px 18px;
  border-radius: 999px;
  background: rgba(255, 251, 242, 0.8);
  border: 1px dashed var(--coral-soft);
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--coral);
  font-size: 0.95rem;
}

.rsvp-card {
  background: rgba(255, 251, 242, 0.78);
  border: 1px solid rgba(255, 251, 242, 0.95);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: clamp(22px, 5vw, 34px);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.form-group { margin-bottom: 18px; }
.form-group label {
  display: block;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 8px;
}
.label-hint {
  text-transform: none;
  letter-spacing: 0.02em;
  font-weight: 300;
  color: var(--ink-faint);
  font-size: 0.8em;
}

.form-control {
  width: 100%;
  padding: 13px 15px;
  font: inherit;
  font-weight: 300;
  font-size: 1rem;
  color: var(--ink);
  background: rgba(255, 253, 249, 0.92);
  border: 1px solid rgba(40, 83, 79, 0.16);
  border-radius: 13px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.form-control::placeholder { color: var(--ink-faint); }
.form-control:focus {
  outline: none;
  border-color: var(--aqua-deep);
  box-shadow: 0 0 0 3px rgba(134, 200, 188, 0.35);
}
textarea.form-control { resize: vertical; min-height: 64px; }

.error {
  display: block;
  margin-top: 6px;
  font-size: 0.8rem;
  color: #C2573F;
  font-weight: 400;
  min-height: 1em;
}

.rsvp-submit-row { margin-top: 6px; text-align: center; }

.btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-sans);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.78rem;
  padding: 15px 34px;
  border-radius: 999px;
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}
.btn--primary {
  color: var(--white);
  background: linear-gradient(135deg, var(--coral), var(--sun));
  box-shadow: 0 14px 28px -12px rgba(239, 143, 116, 0.85);
}
.btn--primary:hover { transform: translateY(-1px); }
.btn--primary:active { transform: translateY(0); }
.btn:disabled { opacity: 0.55; cursor: default; transform: none; }

#rsvp-form .loading,
.loading-guestbook {
  text-align: center;
  color: var(--ink-soft);
  font-size: 0.82rem;
  padding: 14px 0 2px;
  letter-spacing: 0.06em;
}

/* ── 10. Guestbook injected markup ──────────────────────────────────── */
.guestbook-body { margin-top: 4px; }

#guestbookForm {
  background: rgba(255, 251, 242, 0.78);
  border: 1px solid rgba(255, 251, 242, 0.95);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: clamp(22px, 5vw, 34px);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
#guestbookForm .form-group label {
  display: block;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 8px;
}
#guestbookForm button[type="submit"] {
  appearance: none;
  border: 0;
  cursor: pointer;
  display: block;
  margin: 18px auto 0;
  color: var(--white);
  background: linear-gradient(135deg, var(--aqua-deep), var(--seafoam));
  font-family: var(--font-sans);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.78rem;
  padding: 14px 30px;
  border-radius: 999px;
  box-shadow: 0 14px 28px -14px rgba(95, 168, 156, 0.9);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
#guestbookForm button[type="submit"]:hover { transform: translateY(-1px); }
#guestbookForm button[type="submit"]:disabled { opacity: 0.55; }

.guestbook-chapta { margin-top: 14px; min-height: 8px; }

#comments-container.guestbook-wrap {
  margin-top: 26px !important;
  max-height: none !important;
  border-top: 1px solid var(--line);
  padding-top: 8px;
}
.guestbook-entry {
  padding: 18px 4px;
  border-bottom: 1px solid var(--line);
}
.guestbook-entry:last-child { border-bottom: 0; }
.guestbook-entry h5 {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 1.05rem;
  color: var(--ink);
  margin-bottom: 3px;
}
.guestbook-entry p { font-size: 0.95rem; color: var(--ink-soft); margin-bottom: 4px; }
.guestbook-entry small { font-size: 0.72rem; color: var(--ink-faint); letter-spacing: 0.04em; }

.more-guestbook-wrap { text-align: center; margin-top: 18px; }
#load-more {
  appearance: none;
  border: 1px solid var(--aqua-deep);
  background: transparent;
  color: var(--aqua-deep);
  font-family: var(--font-sans);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.72rem;
  padding: 12px 26px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}
#load-more:hover { background: rgba(95, 168, 156, 0.12); }

/* ── 11. RSVP injected states (thank-you / closed) ──────────────────── */
.thank-you-message {
  text-align: center;
  padding: 30px 10px;
  font-family: var(--font-sans);
}
.thank-you-message h3 {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 1.9rem;
  color: var(--ink);
  margin-bottom: 10px;
}
.thank-you-message p {
  color: var(--ink-soft);
  font-size: 0.95rem;
  margin: 4px auto;
  max-width: 40ch;
}
.thank-you-message small {
  display: block;
  margin-top: 20px;
  color: var(--ink-faint);
  line-height: 1.6;
}

/* ── 12. Personalisation ────────────────────────────────────────────── */
.live-region {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ── 12b. Brand footer (placeholder) ──────────────────────────────────
   No-JS: a plain closing line at the end of the stacked document.
   JS shell: hidden where it starts (after </main>), then main.js
   tucks it into the last panel that actually survives platform
   processing, so it always reads as the invitation's closing line.
   Bind a morph to .footer-brand (or let the platform inject it). */
html:not(.js) .app-footer {
  display: block;
  padding: 26px 18px 46px;
  text-align: center;
}
html.js .app-footer { display: none; }
html.js .panel .app-footer {
  display: block;
  margin-top: clamp(30px, 6dvh, 46px);
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.footer-brand {
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.footer-brand-name {
  font-family: var(--font-script);
  font-size: 1.25em;
  text-transform: none;
  letter-spacing: 0.03em;
  color: var(--coral);
}

/* ── 12c. Powered-by brand strip ─────────────────────────────────────
   A slim, full-width "Powered by Kundangi" line under the panels in
   the app shell (and at the end of the stacked document without JS).
   The engine swaps the literal Kundangi token inside .link-footer for
   the platform name at render. The line is platform branding — static
   default text, never a host-editable morph. */
.powered-by {
  flex: 0 0 auto;
  width: 100%;
  text-align: center;
  padding: 9px 14px calc(9px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  background: rgba(255, 251, 242, 0.85);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  position: relative;
  z-index: 29;
}
.powered-by .link-footer { color: var(--aqua-deep); }

/* In the app shell the rail sits BELOW this strip, so it no longer needs
   its own home-indicator inset — one inset at the very bottom, not two
   stacked ones. (.rail-on is set by main.js only when a rail exists.) */
.app-ready.rail-on .powered-by { padding-bottom: 9px; }

/* ── 13. Reduced motion ─────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001s !important;
  }
  .film-play { animation: none; }
  .app-ready .panel,
  .app-ready .panel.is-active {
    transition: none !important;
    transform: none !important;
  }
}

/* ── 14. Print ──────────────────────────────────────────────────────── */
@media print {
  .app-ready .app {
    display: block;
    height: auto;
    overflow: visible;
  }
  .app-ready .panel-stack { overflow: visible; }
  .app-ready .panel,
  .panel {
    position: static !important;
    display: block !important;
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
    page-break-after: always;
    break-after: page;
  }
  .panel:last-child { page-break-after: auto; break-after: auto; }
  .app-ready .panel-cover { overflow: visible; }
  .app-ready .panel-cover .cover {
    height: auto;
    overflow: visible;
  }
  .scene, .grain { display: none !important; }
  .panel-nav, .live-region, .photo-chip-tape, #lightbox, .powered-by { display: none !important; }
  .film-note { display: none !important; } /* a "coming soon" note shouldn't print on the keepsake */
  .film-frame iframe { display: none !important; } /* a playing video can't print — the poster prints instead */
  .lightbox-frame.is-zoomed { overflow: visible; transform: none; }
  body { background: #fff; }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
