/* ===== Custom fonts — served locally so they render for every visitor ===== */
/* The Seasons (TrueType) */
@font-face { font-family: 'The Seasons'; font-style: normal; font-weight: 300; font-display: swap; src: url('/fonts/theseasons-light.ttf') format('truetype'); }
@font-face { font-family: 'The Seasons'; font-style: italic; font-weight: 300; font-display: swap; src: url('/fonts/theseasons-lightitalic.ttf') format('truetype'); }
@font-face { font-family: 'The Seasons'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/theseasons-regular.ttf') format('truetype'); }
@font-face { font-family: 'The Seasons'; font-style: italic; font-weight: 400; font-display: swap; src: url('/fonts/theseasons-italic.ttf') format('truetype'); }
@font-face { font-family: 'The Seasons'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/theseasons-bold.ttf') format('truetype'); }
@font-face { font-family: 'The Seasons'; font-style: italic; font-weight: 700; font-display: swap; src: url('/fonts/theseasons-bolditalic.ttf') format('truetype'); }
/* Ovo — free for commercial use */
@font-face { font-family: 'Ovo'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/ovo-regular.ttf') format('truetype'); }
/* New Icon Script — free for personal use (installed but currently unused) */
@font-face { font-family: 'New Icon Script'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/newiconscript-regular.otf') format('opentype'); }

:root {
  --cream: #faf6f0;
  --paper: #fffdf9;
  --ink: #765e4d;
  --muted: #8a8178;
  --gold: #b08d57;
  --gold-soft: #c9b18a;
  --line: #e6ddd0;
  --sage: #6e7a63;
  --danger: #a4553e;
  /* Ovo is the base font for everything except the couple's names. */
  --serif: 'Ovo', Georgia, 'Times New Roman', serif;
  --sans: 'Ovo', Georgia, 'Helvetica Neue', Arial, sans-serif;
  /* The Seasons is used ONLY for the couple's names (uppercase, no variants). */
  --name: 'The Seasons', Georgia, serif;
  --shadow: 0 10px 30px rgba(90, 70, 40, 0.08);
}

* { box-sizing: border-box; }

/* Hide the page scrollbar site-wide (scrolling still works, wheel/touch/keys). */
html, body { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar, body::-webkit-scrollbar { width: 0; height: 0; display: none; }

body {
  margin: 0;
  font-family: var(--sans);
  color: var(--ink);
  background: #fdf8f5; /* soft warm white behind the content sections */
  line-height: 1.6;
  font-weight: 400; /* Ovo ships a single weight */
}

a { color: var(--gold); }

.script,
.section-title,
.names,
.admin-title,
.card h3 {
  font-family: var(--serif);
}

/* ---------- shared decorative bits ---------- */

.divider {
  color: var(--gold-soft);
  font-size: 1.4rem;
  margin: 1.2rem 0;
  letter-spacing: 0.5rem;
}
.divider.small { font-size: 1rem; margin: 0.6rem 0 1.6rem; }

.monogram {
  font-family: var(--serif);
  font-size: 2.4rem;
  letter-spacing: 0.15rem;
  color: var(--gold);
}
.monogram span { color: var(--gold-soft); font-style: italic; }
.monogram.small { font-size: 1.4rem; }

.amp { color: var(--gold-soft); font-style: italic; }

.btn {
  display: inline-block;
  font-family: var(--sans);
  font-weight: 400;
  letter-spacing: 0.12rem;
  text-transform: uppercase;
  font-size: 0.8rem;
  background: var(--gold);
  color: #fff;
  border: none;
  padding: 0.85rem 2rem;
  border-radius: 2px;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.2s, transform 0.1s;
}
.btn:hover { background: #9a7a48; }
.btn:active { transform: translateY(1px); }
.btn.ghost { background: transparent; color: var(--gold); border: 1px solid var(--gold-soft); }
.btn.tiny { padding: 0.4rem 0.8rem; font-size: 0.68rem; }
.btn.danger { background: transparent; color: var(--danger); border: 1px solid #d9b7ab; }
.btn.danger:hover { background: var(--danger); color: #fff; }

/* ---------- landing / not-found ---------- */

.landing-body { display: flex; min-height: 100vh; align-items: center; justify-content: center; }
/* Landing (no-invite) rides higher instead of vertically centred */
.landing-body.landing-invite { align-items: flex-start; }
.landing-invite .landing { padding-top: clamp(4rem, 11vh, 7.5rem); }
.landing {
  text-align: center;
  max-width: 540px;
  padding: 3rem 1.5rem;
}
.landing .script { font-size: clamp(2.5rem, 7vw, 4rem); margin: 1rem 0 0.3rem; font-weight: 500; }
.tagline { font-size: 1.1rem; color: var(--muted); letter-spacing: 0.1rem; }
.date-line { font-family: var(--serif); font-size: 1.3rem; letter-spacing: 0.06rem; }
.hint { color: var(--muted); font-size: 0.95rem; margin-top: 1.5rem; }

/* No-invite landing — matches the invitation aesthetic (intro-bg + scrim,
   The Seasons names, Ovo body, wax seal). */
.landing-invite {
  position: relative;
  color: #fdf8f5;
  background-color: #23252b;
  background-image:
    linear-gradient(rgba(28, 20, 24, 0.55), rgba(28, 20, 24, 0.66)),
    var(--img-intro, none),
    linear-gradient(180deg, #cdd4dc 0%, #8f99a4 45%, #5c6570 100%);
  background-size: cover;
  background-position: center;
}
.landing-invite .landing { color: #fdf8f5; max-width: 640px; }
.li-eyebrow { font-family: var(--sans); text-transform: lowercase; letter-spacing: normal; font-size: 0.9rem; color: var(--wax); opacity: 0.9; margin: 0 0 1rem; }
.li-names { font-family: var(--name); font-weight: 400; font-style: normal; text-transform: uppercase; letter-spacing: normal; font-size: clamp(1.7rem, 6.4vw, 3.6rem); line-height: 1.1; white-space: nowrap; margin: 0 0 1.1rem; }
.li-date { font-family: var(--sans); letter-spacing: normal; font-size: clamp(0.95rem, 2.4vw, 1.2rem); margin: 0 0 0.3rem; }
.li-loc { font-family: var(--serif); font-size: 1rem; color: #efe7df; margin: 0 0 1.8rem; }
/* Static closed envelope (with its wax seal) as the centrepiece */
.li-envelope { position: relative; width: clamp(230px, 42vw, 360px); aspect-ratio: 2000 / 1442; margin: 0.4rem auto 0.9rem; }
.li-env-img { position: absolute; left: 0; top: 0; width: 100%; height: auto; user-select: none; -webkit-user-drag: none; }
.li-seal { position: absolute; left: 50%; top: 58%; transform: translate(-50%, -50%); width: 30%; height: auto; filter: drop-shadow(0 5px 10px rgba(0,0,0,0.4)); }
.li-hint { font-family: var(--sans); text-transform: lowercase; letter-spacing: normal; font-size: 0.92rem; line-height: 1.6; color: #e6d9cf; opacity: 0.9; max-width: 42ch; margin: 0 auto; }

/* ============================================================
   REDESIGN — burgundy envelope invitation
   ------------------------------------------------------------
   Drop-in image assets: every ".slot" below has a --img variable
   with an on-brand gradient placeholder behind it. To use a real
   photo, drop the file at the path shown and (if the name differs)
   update the matching --img variable here. When --img resolves to a
   real file it paints on top of the placeholder automatically.
   ============================================================ */
:root {
  --burgundy: #701d2d;
  --burgundy-dk: #521018;
  --burgundy-lt: #8a2a38;
  --wax: #ece3d6;
  --wax-sh: #cdbfa8;

  /* Image slots — set these to url('/img/xxx.jpg') when assets are ready */
  --img-intro:     url('/img/intro-bg.png');       /* spans stage + story */
  --img-floral:    url('/img/floral-bg.jpg');      /* (unused now)       */
  --img-card:      url('/img/card-portrait.jpg');  /* invitation photo   */
  --img-story:     url('/img/story.jpg');          /* (unused now)       */
  --img-programme: url('/img/programme.jpg');      /* programme photo    */
  --img-faq:       url('/img/faq.jpg');            /* faq photo          */
}

/* placeholder tints so empty slots look deliberate, not broken */
.slot { background-repeat: no-repeat; background-position: center; background-size: cover; }

/* Sections fade + rise into view as you scroll (JS adds .reveal, then .in) */
.reveal { opacity: 0; transform: translateY(28px); transition: opacity 0.9s ease, transform 0.9s ease; }
.reveal.in { opacity: 1; transform: none; }

.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 1.6rem;
  transform: translateX(-50%);
  z-index: 3;
  color: var(--wax);
  font-size: 1.5rem;
  line-height: 1;
  text-decoration: none;
  opacity: 0.85;
  animation: cue-bob 1.9s ease-in-out infinite;
}
.scroll-cue[hidden] { display: none; }
.scroll-cue:hover { opacity: 1; }
@keyframes cue-bob {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 8px); }
}

/* ---------- landscape "please rotate" notice ---------- */
/* Hidden by default; shown only on touch phones held in landscape (short height).
   The pointer:coarse + max-height gate keeps it off desktops, laptops, and
   tablets — it fires only on actual phones turned sideways. */
.rotate-notice { display: none; }
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  /* Freeze the page behind the overlay so it can't be scrolled in landscape —
     but ONLY on the pages that actually show the overlay (invite + landing).
     The admin page has no overlay and must stay usable/scrollable in landscape. */
  html:has(.rotate-notice), .invite-body, .landing-body { overflow: hidden; height: 100%; }
  .rotate-notice {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 9999;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 1.5rem;
    background: #23252b;
    color: #fdf8f5;
  }
  .rotate-notice .rn-icon {
    display: block;
    font-size: 2.4rem;
    margin: 0 0 0.9rem;
    animation: rn-tip 2.2s ease-in-out infinite;
  }
  .rotate-notice p {
    margin: 0;
    font-family: var(--serif);
    text-transform: lowercase;
    font-size: 1.1rem;
    letter-spacing: 0.02rem;
    line-height: 1.5;
    max-width: 22ch;
  }
}
@keyframes rn-tip {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(-90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .rotate-notice .rn-icon { animation: none; }
}

/* ---------- 1+2. envelope stage (image assets) ---------- */

/* Page stays locked on the envelope until the seal is clicked (JS toggles) */
html.intro-locked, html.intro-locked body { overflow: hidden; height: 100%; }

/* One photo (intro-bg) sits behind BOTH the envelope stage and Our Story */
.intro {
  position: relative;
  background-color: #23252b;
  background-image:
    var(--img-intro, none),
    linear-gradient(180deg, #cdd4dc 0%, #8f99a4 45%, #5c6570 100%);
  background-size: cover;
  background-position: center top;
}

.stage {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 1.5rem;
  overflow: hidden;
  background: transparent; /* let the .intro photo show through */
}

/* Container is sized to the CLOSED envelope (2000x1442). The taller open
   frames (square) are bottom-anchored, so they overflow upward as the card
   rises. Width capped by 66vh so the open state always fits the viewport. */
.envelope {
  position: relative;
  width: min(92vw, 86vh, 780px);
  aspect-ratio: 2000 / 1442;
  transition: transform 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* on open, slide down so the taller open frame is centred / fully visible */
.envelope.is-opening { transform: translateY(18%); }
.env-img {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
  transition: opacity 0.6s ease;
}
.env-closed { z-index: 2; opacity: 1; }
.env-start  { z-index: 3; opacity: 0; }
.env-final  { z-index: 4; opacity: 0; }

.env-caption {
  position: absolute;
  left: 0; right: 0; top: 20%;
  text-align: center;
  color: var(--wax);
  z-index: 5;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.env-love { font-family: var(--sans); text-transform: lowercase; letter-spacing: 0.12rem; font-size: 0.9rem; opacity: 0.9; margin: 0 0 0.4rem; }
.env-names { font-family: var(--name); font-weight: 400; font-style: normal; font-size: clamp(1.2rem, 3.2vw, 1.8rem); letter-spacing: 0.14rem; text-transform: uppercase; margin: 0; white-space: nowrap; }

/* wax seal button (image asset) */
.seal {
  position: absolute;
  left: 50%; top: 60%;
  transform: translate(-50%, -50%);
  /* ===== EDIT SEAL SIZE HERE =====
     The middle value (30%) is the knob = seal width as a % of the envelope.
     Bigger % → bigger seal. (84px / 220px are just the min / max clamps.) */
  width: clamp(84px, 30%, 220px);
  aspect-ratio: 1;
  border: none;
  background: none;
  padding: 0;
  z-index: 6;
  cursor: pointer;
  filter: drop-shadow(0 5px 10px rgba(0,0,0,0.45));
  transition: transform 0.25s ease, opacity 0.5s ease;
}
.seal img { width: 100%; height: 100%; display: block; }
.seal:hover { transform: translate(-50%, -50%) scale(1.06); }
.seal:active { transform: translate(-50%, -50%) scale(0.96); }

/* opening sequence: JS adds .is-opening, then .is-open a beat later */
.envelope.is-opening .env-closed { opacity: 0; }
.envelope.is-opening .env-start  { opacity: 1; }
.envelope.is-open    .env-start  { opacity: 0; }
.envelope.is-open    .env-final  { opacity: 1; }
.envelope.is-opening .seal,
.envelope.is-opening .env-caption { opacity: 0; pointer-events: none; }

/* sits on the lower body of the closed envelope (child of .envelope) */
.open-hint {
  position: absolute;
  left: 0; right: 0; bottom: 9%;
  text-align: center;
  color: var(--wax);
  font-family: var(--sans);
  text-transform: lowercase;
  letter-spacing: 0.12rem;
  /* ===== EDIT "OPEN THE INVITATION" HORIZONTAL OFFSET HERE =====
     Positive value nudges the text RIGHT, negative nudges it LEFT.
     (Compensates the letter-spacing + the slightly off-centre envelope art.) */
  text-indent: 0.6rem;
  font-size: clamp(0.78rem, 1.7vw, 0.95rem);
  opacity: 0.82;
  z-index: 5;
  animation: hint-bob 2.4s ease-in-out infinite;
  transition: opacity 0.4s ease;
}
.open-hint.gone { opacity: 0; pointer-events: none; animation: none; }

/* "RSVP HERE" box on the open envelope's lower pocket (child of .envelope).
   Hidden until the card is fully open, then it fades in. Clicking it unlocks
   the page and scrolls down (see rsvp.js). */
.env-rsvp {
  position: absolute;
  left: 50%;
  /* ===== EDIT "RSVP HERE" VERTICAL POSITION HERE =====
     Bigger % → higher up the envelope. */
  bottom: 8%;
  transform: translateX(-50%) translateY(8px);
  z-index: 6;
  font-family: var(--sans);
  text-transform: uppercase;
  letter-spacing: 0.22rem;
  text-indent: 0.22rem; /* offsets the trailing letter-spacing so text sits centered */
  font-size: clamp(0.72rem, 1.6vw, 0.92rem);
  color: #fdf8f5;
  background: transparent;
  border: 1px solid #fdf8f5;
  padding: 0.72rem 1.7rem;
  border-radius: 2px;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease 0.35s, transform 0.5s ease 0.35s,
              background 0.2s ease, color 0.2s ease;
}
.envelope.is-open .env-rsvp {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}
.env-rsvp:hover { background: #fdf8f5; color: var(--burgundy); }
.env-rsvp:active { transform: translateX(-50%) translateY(1px); }
/* Y-only bob — the full-width hint must NOT reuse cue-bob's translate(-50%) */
@keyframes hint-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

/* ---------- 3. our story (full-bleed) ---------- */

.story-hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  /* ===== EDIT GAP BETWEEN STORY TEXT AND DATE/LOCATION HERE =====
     Bigger value pushes date/time + location further down the photo. */
  gap: clamp(3rem, 12vh, 7rem);
  /* top value raised to lower the whole block toward the horizon */
  padding: clamp(5rem, 14vh, 8.5rem) clamp(1.5rem, 6vw, 5rem) clamp(2.5rem, 6vw, 4.5rem);
  color: #f3efe9;
  text-align: center;
  /* transparent to the .intro photo; scrim darkens for white-text legibility.
     Starts at 0 opacity so it blends seamlessly from the stage above (no seam),
     then ramps in behind the names and again at the bottom (date/location). */
  background: linear-gradient(rgba(28,32,42,0) 0%, rgba(28,32,42,0.28) 35%, rgba(28,32,42,0.26) 100%);
}
.story-top { max-width: 1200px; margin: 0 auto; }
.story-names { font-family: var(--name); font-weight: 400; font-style: normal; font-size: clamp(2rem, 6vw, 3.4rem); letter-spacing: 0.06rem; text-transform: uppercase; margin: 0 0 1.2rem; }
/* ===== EDIT STORY PARAGRAPH WIDTH HERE (higher ch = longer lines / fewer rows) ===== */
.story-prose { font-family: var(--serif); font-size: 0.98rem; line-height: 1.5; max-width: 100ch; margin: 0 auto; }
.story-meta { display: flex; justify-content: space-between; gap: 2rem; max-width: 1000px; margin: 0 auto; width: 100%; }
.story-block h3 { font-family: var(--serif); font-weight: 400; text-transform: lowercase; font-size: clamp(1.2rem, 3vw, 1.7rem); margin: 0 0 0.1rem; }
.story-block p { margin: 0.1rem 0; font-size: 0.98rem; }

/* Mobile-only scrollytelling for Our Story — hidden on desktop (shown in the mobile media query). */
.story-scrolly { display: none; }

/* ---------- shared section headings ---------- */

.block-title { font-family: var(--serif); font-weight: 400; text-transform: lowercase; font-size: clamp(1.6rem, 4vw, 2.3rem); color: var(--burgundy); margin: 0 0 1.6rem; }
.block-title.sm { font-size: clamp(1.2rem, 3vw, 1.5rem); margin: 0 0 0.4rem; }
.block-title.center { text-align: center; }

.section { max-width: 1080px; margin: 0 auto; padding: clamp(3rem, 8vw, 5.5rem) clamp(1.5rem, 6vw, 4rem); }

/* ---------- 4. programme ---------- */

.programme { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 6vw, 4.5rem); align-items: start; }
.timeline { list-style: none; padding: 0; margin: 1.5rem 0 0; }
.tl-item { display: grid; grid-template-columns: 6.5rem 1fr; gap: 1rem; padding: 1.1rem 0; align-items: baseline; border-bottom: 1px dotted var(--line); }
.tl-time { font-family: var(--serif); font-size: 1.05rem; color: var(--ink); }
.tl-label { font-family: var(--serif); font-size: 1.05rem; color: var(--ink); }
.prog-photo { aspect-ratio: 4 / 3; border-radius: 4px; margin-bottom: 1.8rem; background-image: var(--img-programme, none), linear-gradient(135deg, #cdd6df, #9fb3c4); box-shadow: var(--shadow); }
/* ===== EDIT GAP BETWEEN DRESS CODE AND AFTER PARTY HERE ===== */
.prog-note { margin-bottom: 2.8rem; }
.prog-note:last-child { margin-bottom: 0; }
/* header-to-text gap matches the story "location" block (0.1rem) */
.prog-note .block-title.sm { margin-bottom: 0.1rem; }
.prog-note p { margin: 0; color: var(--ink); font-family: var(--serif); font-size: 1.02rem; }

/* ---------- 5. faq ---------- */

.faq { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: clamp(2rem, 6vw, 4.5rem); align-items: start; }
.faq-photo { aspect-ratio: 3 / 4; border-radius: 4px; background-image: var(--img-faq, none), linear-gradient(135deg, #cfe0ea, #a9c4d6); box-shadow: var(--shadow); }
.faq-list { list-style: none; padding: 0; margin: 1.5rem 0 0; }
.faq-item { display: grid; grid-template-columns: 2.6rem 1fr; gap: 0.8rem; margin-bottom: 1.8rem; }
.faq-num { font-family: var(--serif); font-size: 1.1rem; color: var(--muted); }
.faq-q { font-family: var(--serif); font-weight: 400; font-size: 1.25rem; color: var(--ink); margin: 0 0 0.35rem; }
.faq-a { margin: 0; font-size: 0.95rem; color: var(--muted); line-height: 1.55; }

/* ---------- rsvp (kept functional) ---------- */

.section.rsvp { max-width: 620px; text-align: center; }
.rsvp-intro { font-family: var(--serif); font-weight: 400; font-size: clamp(1.5rem, 3.6vw, 2.1rem); line-height: 1.3; color: var(--burgundy); text-transform: lowercase; white-space: nowrap; width: max-content; position: relative; left: 50%; transform: translateX(-50%); margin: 0 0 1rem; }
.countdown { font-family: var(--serif); font-size: 1.2rem; color: var(--sage); letter-spacing: 0.05rem; margin: 0.4rem 0 1.8rem; }

/* names helper still used by landing.ejs */
.names { font-family: var(--name); font-style: normal; text-transform: uppercase; display: flex; flex-direction: column; align-items: center; gap: 0.05rem; margin: 0.5rem 0; line-height: 1.02; }
.names .nm { font-size: clamp(2.4rem, 7.5vw, 4.4rem); font-weight: 400; white-space: nowrap; }
.names .amp-line { font-size: clamp(1.3rem, 3.5vw, 2rem); color: var(--gold-soft); font-style: italic; margin: 0.15rem 0; }

@media (prefers-reduced-motion: reduce) {
  .scroll-cue, .open-hint, .ss-cue { animation: none; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .env-img { transition: none; }
}

/* ---------- redesign responsive ---------- */
@media (max-width: 760px) {
  .programme, .faq { grid-template-columns: 1fr; }
  .faq-photo { position: relative; top: 0; max-width: 320px; }
  .story-meta { flex-direction: column; gap: 1.4rem; text-align: center; }
  /* On phones this line must wrap instead of overflowing the screen.
     Drop the max-content centering (only needed to keep it one line on desktop).
     NOTE: .done-msg gets the same treatment lower down, after its base rule,
     because that rule is defined later in the file and would otherwise win. */
  .rsvp-intro {
    white-space: normal;
    width: auto;
    left: auto;
    transform: none;
    max-width: 100%;
  }
}

/* ---------- mobile (phone) tuning — MOBILE ONLY ---------- */
@media (max-width: 640px) {
  /* 1. Much bigger envelope, centred by .stage's flexbox; sides spill past the
        viewport and get clipped by .stage overflow:hidden (no page scroll).
        flex: 0 0 auto stops the flexbox from shrinking it back to fit. */
  .envelope { width: 135vw; flex: 0 0 auto; }
  /* The stage BOX must be a full 100vh (not svh) so its bottom edge lands on the
     same photo row (200vh photo -> row 100vh) where the pinned scrolly's crop
     begins — that's what makes the shared photo continuous across the seam.
     To still center the envelope inside the VISIBLE area (svh, excluding the
     address bar), pad the bottom by exactly the address-bar height (100vh-100svh);
     where svh isn't supported this collapses to 0 and centres in 100vh. */
  .stage { min-height: 100vh; padding: 1rem 0 calc(100vh - 100svh); }

  /* 2. "open the invitation": tighter kerning, smaller, truly centred. */
  .open-hint {
    letter-spacing: 0.02rem;
    text-indent: 0;           /* was compensating the wide kerning */
    font-size: 0.7rem;
  }

  /* 3. RSVP button a little bigger — but far less than the envelope grew. */
  .env-rsvp {
    font-size: 1rem;
    letter-spacing: 0.18rem;
    text-indent: 0.18rem;
    padding: 0.85rem 2.1rem;
  }

  /* 4. Our Story becomes the pinned scrollytelling section below; hide the
        desktop story block. Give the envelope stage its own copy of the photo
        (top/sky crop) so hiding .story-hero doesn't reframe the shared background. */
  .story-hero { display: none; }
  .stage {
    /* NO scrim — this is the TOP half of one continuous photo. The photo is scaled
       to 200vh tall and top-aligned, so the stage (100vh box) shows rows [0..100vh];
       the pin below shows [100vh..200vh]. No darkening anywhere = no seam shadow.
       vh (not dvh) keeps the scale stable while the address bar shows/hides. */
    background-image:
      var(--img-intro, none),
      linear-gradient(180deg, #cdd4dc 0%, #8f99a4 45%, #5c6570 100%);
    background-size: auto 200vh, cover;
    background-position: center top, center top;
    background-repeat: no-repeat, no-repeat;
  }

  /* ----- pinned scrollytelling: title + couple photo stay put while the box
           cycles through prose(1), prose(2), date/time, location ----- */
  .story-scrolly {
    display: block;
    position: relative;
    height: 196vh;               /* scroll distance for the 4 dwell steps (~24vh dwell each) */
    background: #23252b;
  }
  .ss-pin {
    position: sticky;
    top: 0;
    /* vh (large viewport, fixed) not dvh — a dvh height changes with the address
       bar during scroll, which both rescales the photo and shifts the layout. */
    height: 100vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 1.6rem 1.25rem 0;
    text-align: center;
    color: #fdf8f5;
    /* NO scrim — this is the BOTTOM half of the same continuous photo (200vh tall,
       bottom-aligned -> shows rows [100vh..200vh], continuing exactly from the
       stage above). Text legibility is handled by text-shadow on the text itself
       (below), so the background stays scrim-free and the seam has zero shadow. */
    background-image:
      var(--img-intro, none),
      linear-gradient(180deg, #5c6570, #23252b);
    background-size: auto 200vh, cover;
    background-position: center bottom, center;
    background-repeat: no-repeat;
  }
  .ss-names {
    font-family: var(--name); font-weight: 400; font-style: normal;
    text-transform: uppercase; letter-spacing: normal;
    font-size: clamp(1.7rem, 6.4vw, 3.6rem); line-height: 1.1; margin: 0;
  }
  /* the usable box between the title and the couple's heads */
  .ss-box {
    position: relative;
    width: 100%;
    max-width: 30rem;
    margin-top: 1.6rem;
    height: 30vh;
  }
  .ss-panel {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 0.15rem;
    margin: 0;
    opacity: 0;
    transition: opacity 0.25s ease;   /* quick "snap" between pieces */
    pointer-events: none;
  }
  .ss-panel.is-active { opacity: 1; }
  p.ss-panel { font-family: var(--serif); font-size: 0.85rem; line-height: 1.55; }
  .ss-panel .ss-h { font-family: var(--serif); font-size: 1.45rem; margin-bottom: 0.2rem; }
  .ss-panel .ss-t { font-size: 0.95rem; }
  /* tiny "keep scrolling" chevron below the box (Y-only bob so it stays centred) */
  .ss-cue {
    margin-top: 0.5rem;
    font-size: 1.4rem;
    line-height: 1;
    color: #fdf8f5;
    opacity: 0.7;
    animation: hint-bob 2s ease-in-out infinite;
  }

  /* Even vertical rhythm down the programme column on phones: the gap
     image->dress code, dress code->after party, and after party->next image
     are all made equal. The last two gaps are driven by margins (below), so
     zero the section paddings that otherwise pad the after-party->FAQ seam. */
  .prog-photo { margin-bottom: 2.2rem; }              /* image -> dress code   */
  .prog-note { margin-bottom: 2.2rem; }               /* dress code -> after party */
  .prog-note:last-child { margin-bottom: 2.2rem; }    /* after party -> next image */
  .programme { padding-bottom: 0; }
  .faq { padding-top: 0; }

}

/* ---------- rsvp form ---------- */

.rsvp-form { max-width: 520px; margin: 1.5rem auto 0; text-align: left; }
.rsvp-form[hidden] { display: none; }
/* Send RSVP: brown (matches the form text) and centred in the form. */
.rsvp-form .btn { background: var(--ink); display: block; width: fit-content; margin: 0.5rem auto 0; }
.rsvp-form .btn:hover { background: #5f4c3e; }
.thanks-note { text-align: center; color: var(--sage); }

.rsvp-done { text-align: center; padding: 1.5rem 0 0.5rem; }
.rsvp-done[hidden] { display: none; }
.done-msg { font-size: clamp(1.4rem, 4vw, 1.9rem); color: var(--ink); text-transform: lowercase; white-space: nowrap; width: max-content; position: relative; left: 50%; transform: translateX(-50%); margin: 0 0 1.4rem; line-height: 1.3; }
/* Phones: let the confirmation line wrap (placed after the base rule so it wins) */
@media (max-width: 760px) {
  .done-msg { white-space: normal; width: auto; left: auto; transform: none; max-width: 100%; }
}
/* Second half of the RSVP title / confirmation line: inline on desktop (one
   line), forced onto its own line on phones. */
.mob-line2 { display: inline; }
@media (max-width: 640px) {
  .mob-line2 { display: block; }
}
.add-cal { display: inline-block; font-family: var(--sans); text-transform: lowercase; letter-spacing: 0.03rem; font-size: 1rem; color: var(--burgundy); text-decoration: underline; text-underline-offset: 3px; margin: 0 0 1.4rem; }
.add-cal[hidden] { display: none; }
.add-cal:hover { color: var(--burgundy-dk); }
/* Phones: the shorter "Edit your response" label lets the calendar link and the
   edit button fit on one line — force the link to its own line so they stack.
   After the base rule so it wins by order; [hidden] still beats it by specificity. */
@media (max-width: 640px) {
  .add-cal { display: block; }
}
#edit-rsvp { font-size: 0.72rem; }
/* Desktop shows the full question; phones swap to the short label. The mobile
   override sits right after this base rule so cascade order lets it win. */
.edit-short { display: none; }
@media (max-width: 640px) {
  .edit-full { display: none; }
  .edit-short { display: inline; }
}
.field { border: none; padding: 0; margin: 0 0 1.8rem; }
.field legend {
  font-family: var(--serif);
  font-size: 1.25rem;
  color: var(--ink);
  margin-bottom: 0.7rem;
  padding: 0;
}
.radio, .check { display: flex; align-items: center; gap: 0.6rem; padding: 0.35rem 0; cursor: pointer; }
.checks { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.2rem 1rem; margin-bottom: 0.8rem; }
.radio input, .check input { accent-color: var(--ink); width: 1.05rem; height: 1.05rem; }

.text-input {
  width: 100%;
  font-family: var(--sans);
  font-size: 1rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--paper);
  color: var(--ink);
  margin-top: 0.4rem;
}
.text-input:focus { outline: none; border-color: var(--gold-soft); box-shadow: 0 0 0 3px rgba(201,177,138,0.2); }

.rsvp-status { margin-top: 1rem; font-family: var(--serif); font-size: 1.15rem; min-height: 1.4rem; }
.rsvp-status.success { color: var(--sage); }
.rsvp-status.error { color: var(--danger); }
.rsvp-status.sending { color: var(--muted); }

/* ---------- footer ---------- */

.site-footer { text-align: center; padding: clamp(1rem, 2.4vw, 1.5rem) 1.5rem; background: var(--burgundy); position: relative; }
/* ===== EDIT FOOTER SEAL SIZE HERE (middle value = seal width) ===== */
/* Negative top margin lifts the seal above the footer's top edge so it looks
   like it's "sealing" the footer. Make it MORE negative to pop it up further. */
.footer-seal { width: clamp(63px, 8.4vw, 105px); height: auto; display: block; margin: -4.6rem auto 0; position: relative; z-index: 2; }
/* Phones: the -4.6rem pull is sized for the 105px desktop seal and yanks the
   smaller 63px mobile seal entirely off the footer. Pull up ~half its height so
   it still pokes above the edge. Placed AFTER the base rule so the margin
   shorthand above doesn't reset our margin-top (cascade order, not specificity). */
@media (max-width: 640px) {
  .footer-seal { margin-top: -3.0rem; }
}
.footer-date { margin: 0; font-family: var(--sans); color: var(--wax); letter-spacing: 0.25rem; font-size: clamp(0.85rem, 2vw, 1.05rem); }

/* ---------- admin ---------- */

/* Match the invitation's palette. Base accents (stat numbers, links, icons, the
   small table Save button) use brown via the retinted gold token; prominent
   buttons and section headers use maroon. Invite pages use a different body
   class, so they keep their own gold. */
.admin-body { background: var(--cream); --gold: #765e4d; --gold-soft: #a98a6f; }

/* Section headers + page title in maroon. */
.admin-body .admin-title,
.admin-body .panel h2,
.admin-body .sum-h,
.admin-body .modal-box h3 { color: var(--burgundy); }

/* Big buttons in maroon: solid ones fill, ghost ones outline. The tiny table
   buttons (Save/Delete/Copy) are excluded and keep their own styling. */
.admin-body .btn:not(.tiny):not(.ghost) { background: var(--burgundy); color: #fff; }
.admin-body .btn:not(.tiny):not(.ghost):hover { background: var(--burgundy-dk); }
.admin-body .btn.ghost { color: var(--burgundy); border-color: var(--burgundy); }
.admin-body .btn.ghost:hover { background: var(--burgundy); color: #fff; }

/* Small solid table button (Save) stays brown with a darker-brown hover, and
   gets a transparent 1px border so its box height matches the outlined Delete
   button beside it — that's what lines up their top/bottom edges. */
.admin-body .btn.tiny:not(.danger):not(.invite-btn) { border: 1px solid transparent; }
.admin-body .btn.tiny:not(.ghost):not(.danger):not(.invite-btn):hover { background: #5f4c3e; }

/* Copy invite is now a compact emoji icon (no button chrome). */
.admin-body .invite-btn { background: none; border: none; box-shadow: none; min-width: 0; padding: 0.2rem 0.4rem; font-size: 1.15rem; line-height: 1; color: inherit; }
.admin-body .invite-btn:hover { background: none; }
.admin-body .invite-btn.copied { background: none; color: var(--sage); }

/* Clickable guest name in the Responses table (opens the edit modal). */
.admin-body .resp-name { background: none; border: none; padding: 0; font: inherit; color: var(--burgundy); cursor: pointer; text-decoration: none; }
.admin-body .resp-name:hover { color: var(--burgundy-dk); }

/* Larger modal for the edit form. */
.modal-box-lg { max-width: 520px; max-height: 85vh; overflow-y: auto; text-align: left; }
.edit-form .field { margin-bottom: 1.2rem; }
/* +1 details: the guest name sits directly above the dietary checkboxes with no
   gap — add one so it matches the spacing below the checkboxes. Adjacent-sibling
   selector targets only checks that follow an input (not the main dietary list,
   which follows its legend). */
.edit-form .text-input + .checks { margin-top: 1.2rem; }
.edit-actions { display: flex; gap: 0.8rem; margin-top: 0.5rem; }

/* ---------- admin: phone tuning ---------- */
@media (max-width: 640px) {
  /* Keep the page title and Sign-out on one line each, side by side. */
  .admin-header { flex-wrap: nowrap; gap: 0.75rem; }
  .admin-body .admin-title { font-size: 1.35rem; white-space: nowrap; }
  .admin-header .btn { white-space: nowrap; flex: 0 0 auto; }
  /* "Download Excel" -> "Excel" to save space in the responses header. */
  .admin-body .dl-word { display: none; }
}
.admin-login { max-width: 380px; margin: 12vh auto; text-align: center; padding: 2rem; }
.admin-title { font-size: 2rem; font-weight: 500; margin: 0.5rem 0 1.5rem; }
.login-form { display: flex; flex-direction: column; gap: 1rem; }
.login-form input { padding: 0.8rem 1rem; border: 1px solid var(--line); border-radius: 3px; font-size: 1rem; }
.form-error { color: var(--danger); }

.admin-wrap { max-width: 1080px; margin: 0 auto; padding: 2rem 1.5rem 5rem; }
.admin-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; }
.admin-header .admin-title { margin: 0; }

.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 2rem; }
.stat { background: var(--paper); border: 1px solid var(--line); border-radius: 4px; padding: 1.2rem; text-align: center; box-shadow: var(--shadow); }
.stat .num { display: block; font-family: var(--serif); font-size: 2.2rem; color: var(--gold); line-height: 1; }
.stat .lbl { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.12rem; color: var(--muted); }

.panel { background: var(--paper); border: 1px solid var(--line); border-radius: 6px; padding: 1.5rem; margin-bottom: 1.5rem; box-shadow: var(--shadow); }
.panel h2 { font-family: var(--serif); font-weight: 500; font-size: 1.5rem; margin: 0 0 1rem; }
.muted { color: var(--muted); }

.inline-form { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.inline-form input[type="text"] { flex: 1; min-width: 200px; padding: 0.6rem 0.8rem; border: 1px solid var(--line); border-radius: 3px; font-size: 1rem; }

.table-scroll { overflow-x: auto; }
.guest-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.guest-table th { text-align: left; font-weight: 400; text-transform: uppercase; letter-spacing: 0.08rem; font-size: 0.72rem; color: var(--muted); border-bottom: 1px solid var(--line); padding: 0.6rem 0.7rem; }
.guest-table td { border-bottom: 1px solid var(--line); padding: 0.6rem 0.7rem; vertical-align: middle; }
.guest-table td.center { text-align: center; }
.name-input { width: 100%; min-width: 120px; padding: 0.4rem 0.6rem; border: 1px solid var(--line); border-radius: 3px; font-size: 0.92rem; }
.msg-cell { max-width: 220px; }
.small-txt { font-size: 0.78rem; white-space: nowrap; }

.link-cell { display: flex; gap: 0.4rem; align-items: center; }
.link-input {
  width: 240px;
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  font-size: 0.78rem;
  color: var(--muted);
  background: #fbf8f2;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.15s, background 0.15s;
}
.link-input:hover { border-color: var(--gold-soft); }
.link-input.copied { border-color: var(--sage); background: #eef2e8; color: var(--sage); }

.side-text { white-space: nowrap; font-size: 1.1rem; color: var(--ink); text-align: center; }

/* Responses: filter bar, sortable headers, stacked dietary */
.panel-head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-bottom: 0.5rem; }
.panel-head h2 { margin: 0; }

.filters { display: flex; flex-wrap: wrap; gap: 1rem; margin: 0.5rem 0 1.2rem; }
.filters label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08rem; color: var(--muted); }
.filters select { padding: 0.4rem 0.6rem; border: 1px solid var(--line); border-radius: 3px; font-family: var(--sans); font-size: 0.9rem; background: var(--paper); color: var(--ink); }

th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable:hover { color: var(--gold); }
th.sortable::after { content: '\2195'; opacity: 0.35; margin-left: 0.3rem; font-size: 0.8em; }
th.sortable.sort-asc::after { content: '\2191'; opacity: 1; }
th.sortable.sort-desc::after { content: '\2193'; opacity: 1; }

.diet-line { white-space: nowrap; }
.diet-note { font-style: italic; color: var(--muted); font-size: 0.85rem; margin-top: 0.15rem; }

/* Wide enough to contain "Copy invite" so the swap to "Copied!" never resizes it. */
.copy-btn { min-width: 7rem; text-align: center; white-space: nowrap; }
.copy-btn.copied { background: var(--sage); }

.side-select {
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  font-family: var(--sans);
  font-size: 0.85rem;
  background: var(--paper);
  color: var(--ink);
}

/* Summary panel */
.summary-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.6rem; }
.summary-grid-3 { grid-template-columns: repeat(3, 1fr); }
.sum-card { text-align: center; background: #fbf8f2; border: 1px solid var(--line); border-radius: 4px; padding: 1rem 0.5rem; }
.sum-num { display: block; font-family: var(--serif); font-size: 1.9rem; color: var(--gold); line-height: 1; }
.sum-lbl { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1rem; color: var(--muted); }
.summary-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; }
.sum-h { font-family: var(--serif); font-weight: 500; font-size: 1.15rem; margin: 0.5rem 0 0.6rem; color: var(--ink); }
.sum-list { list-style: none; padding: 0; margin: 0; }
.sum-list li { display: flex; justify-content: space-between; gap: 1rem; padding: 0.35rem 0; border-bottom: 1px dotted var(--line); font-size: 0.92rem; }
.sum-list li b { font-family: var(--serif); color: var(--gold); font-weight: 500; }
.sum-notes { margin: 0.3rem 0 0; padding-left: 1.1rem; color: var(--ink); font-size: 0.92rem; }
.sum-notes li { padding: 0.15rem 0; }

/* Keep the cell as a real table-cell (not flex) so the base vertical-align:middle
   centres the buttons within the row — a flex td sizes to its content and sits at
   the top of taller rows, throwing the Save/Delete off. Lay the two forms out
   inline instead. */
.actions { text-align: center; white-space: nowrap; }
.actions form { display: inline-block; margin: 0; vertical-align: middle; }
.actions form + form { margin-left: 0.4rem; }

.pill { display: inline-block; padding: 0.2rem 0.7rem; border-radius: 999px; font-size: 0.72rem; letter-spacing: 0.05rem; white-space: nowrap; }
.pill.yes { background: #e5ecdd; color: var(--sage); }
.pill.no { background: #f2e0da; color: var(--danger); }
.pill.pending { background: #efe8db; color: var(--muted); }

/* Message icon + modal */
.msg-btn { background: none; border: none; cursor: pointer; font-size: 1.15rem; color: var(--gold); padding: 0.2rem 0.45rem; border-radius: 3px; line-height: 1; }
.msg-btn:hover { background: #f0e7d7; }

.modal { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 50; padding: 1rem; }
.modal[hidden] { display: none; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(51, 48, 43, 0.45); }
.modal-box { position: relative; z-index: 1; background: var(--paper); border-radius: 6px; padding: 2rem; max-width: 460px; width: 100%; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25); }
.modal-box h3 { font-family: var(--serif); font-weight: 500; font-size: 1.3rem; margin: 0 0 0.8rem; color: var(--gold); }
.modal-box p { margin: 0; white-space: pre-wrap; line-height: 1.6; color: var(--ink); }
.modal-close { position: absolute; top: 0.4rem; right: 0.8rem; background: none; border: none; font-size: 1.7rem; line-height: 1; color: var(--muted); cursor: pointer; }
.modal-close:hover { color: var(--ink); }

/* Settings subsections */
.settings-hint { font-size: 0.85rem; margin-top: 0; }
.settings-hint code { background: #f0e7d7; padding: 0.05rem 0.35rem; border-radius: 3px; font-size: 0.85em; }
.message-form { display: flex; flex-direction: column; gap: 0.8rem; align-items: flex-start; }
.rsvp-labels-form { display: flex; flex-direction: column; gap: 0.9rem; align-items: flex-start; max-width: 420px; }
.rsvp-labels-form .stacked-label { display: flex; flex-direction: column; gap: 0.3rem; width: 100%; }
.rsvp-labels-form .stacked-label span { font-size: 0.85rem; color: var(--muted); letter-spacing: 0.03rem; }
.msg-template { width: 100%; font-family: var(--sans); line-height: 1.5; resize: vertical; }
.dietary-list { list-style: none; padding: 0; margin: 0 0 1rem; display: flex; flex-wrap: wrap; gap: 0.6rem; }
.dietary-list li { margin: 0; }
.dietary-chip-form { margin: 0; }
.dietary-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: #fbf8f2; border: 1px solid var(--line); border-radius: 999px;
  padding: 0.35rem 0.95rem; font-family: var(--sans); font-size: 0.9rem; color: var(--ink);
  cursor: pointer; transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.dietary-chip .chip-x { opacity: 0; width: 0; overflow: hidden; font-size: 1.05rem; line-height: 1; transition: opacity 0.15s; }
.dietary-chip:hover { background: var(--danger); border-color: var(--danger); color: #fff; }
.dietary-chip:hover .chip-x { opacity: 1; width: auto; }
.dietary-add { margin-top: 0.5rem; }

/* ---------- responsive ---------- */

@media (max-width: 640px) {
  .cards { grid-template-columns: 1fr; }
  .gallery { grid-template-columns: 1fr 1fr; }
  .stats { grid-template-columns: 1fr 1fr; }
  .inline-form { flex-direction: column; align-items: stretch; }
  .summary-grid { grid-template-columns: 1fr 1fr; }
  .summary-cols { grid-template-columns: 1fr; gap: 1rem; }
}
