/* ═══════════════════════════════════════════════════════════════════════════
   wedding-modern-mono-card — styles.css
   A black & white, typography-led invitation card.
   Reads like a letterpress card set in monospace: double-hairline frame,
   centered classic wording, generous margins.

   Design tokens
   ─────────────────────────────────────────────────────────────────────── */
:root {
  --ink: #0c0c0e;
  --paper: #fdfdfc;
  --muted: #6b6b70;
  --hairline: rgba(12, 12, 14, 0.9);
  --faintline: rgba(12, 12, 14, 0.22);

  --font-mono: "Space Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo,
               Consolas, "Liberation Mono", monospace;

  --chrome-h: 3.25rem;           /* top bar height */
  --rail-w: 9.5rem;              /* desktop right rail width */
  --bottom-bar-h: 3.75rem;       /* mobile bottom bar height */
  --pad-x: clamp(1.25rem, 6vw, 6rem);
  --pad-y: clamp(3rem, 9vh, 6rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --t-fast: 0.28s;
  --t-slow: 0.55s;
}

/* ── Reset & base ─────────────────────────────────────────────────────── */
*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; }
img, svg { max-width: 100%; display: block; }
[hidden] { display: none !important; }

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

a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* ── Shared labels (tiny, tracked-out uppercase) ─────────────────────── */
.k,
.panel__index,
.rsvp-countdown__label,
.chrome,
.panel-nav,
.cover__parents,
.cover__date,
.cover__time,
.cover__tail,
.cover__rsvp-line,
.skip-link,
.rsvp-field label,
.guestbook-section label,
.agenda__time,
.directions__title,
.rsvp-submit,
.loading,
.error {
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  font-weight: 700;
}

/* ── Skip link ────────────────────────────────────────────────────────── */
.skip-link {
  position: fixed;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 100;
  background: var(--ink);
  color: var(--paper);
  padding: 0.6rem 1rem;
  transform: translateY(-300%);
  transition: transform var(--t-fast) var(--ease);
}
.skip-link:focus { transform: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   SHELL — single-viewport experience
   Without JS every panel is a normal block (page scrolls naturally).
   main.js adds `body.panels-on` which locks the shell to 100dvh and lets
   only the active panel scroll internally.
   ═══════════════════════════════════════════════════════════════════════ */
.shell { position: relative; }

.panels > .panel { padding: var(--pad-y) var(--pad-x) calc(var(--pad-y) + 1.5rem); }
.panel--bleed { padding: 0 !important; }

.chrome,
.panel-nav {
  position: fixed;
  z-index: 40;
  background: var(--paper);
}

.chrome {
  top: 0;
  left: 0;
  right: 0;
  height: var(--chrome-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 clamp(1rem, 4vw, 2.5rem);
  border-bottom: 1px solid var(--hairline);
  font-size: 0.625rem;
  letter-spacing: 0.22em;
}
.chrome__brand { display: flex; align-items: baseline; gap: 0.5em; min-width: 0; overflow: hidden; white-space: nowrap; }
.chrome__amp { font-style: italic; font-weight: 700; }
.chrome__sep { opacity: 0.45; }
.chrome__sub { opacity: 0.55; font-weight: 400; }
.chrome__counter { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── Panels: static (no-JS) ───────────────────────────────────────────── */
.panel {
  position: relative;
  background: var(--paper);
}
.panel + .panel { border-top: 1px solid var(--hairline); }

/* ── Optional features off (photo gallery / cover photo / gallery
     frames — see assets/img/README.md). main.js additionally drops
     data-hide panels from the shell + rail. ─────────────────────── */
.panel[data-hide] { display: none !important; }
.cover-photo[data-hide], .moment[data-hide] { display: none !important; }

/* ── Panels: fixed shell (JS on) ──────────────────────────────────────── */
body.panels-on .shell { height: 100vh; height: 100dvh; overflow: hidden; }
body.panels-on .panels { position: absolute; inset: 0; }
body.panels-on .panel {
  position: absolute;
  inset: 0;
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--ink) transparent;
  border-top: 0;

  opacity: 0;
  visibility: hidden;
  transform: translateY(1.25rem);
  transition:
    opacity var(--t-slow) var(--ease),
    transform var(--t-slow) var(--ease),
    visibility 0s linear var(--t-slow);
}
body.panels-on .panel.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity var(--t-slow) var(--ease),
    transform var(--t-slow) var(--ease);
}
body.panels-on .panel::-webkit-scrollbar { width: 6px; }
body.panels-on .panel::-webkit-scrollbar-thumb { background: var(--ink); }
body.panels-on .panel::-webkit-scrollbar-track { background: transparent; }

/* desktop: keep content clear of the right rail */
@media (min-width: 1024px) {
  body.panels-on .panels > .panel { padding-right: calc(var(--rail-w) + var(--pad-x)); }
  body.panels-on .panel--bleed { padding: 0 !important; }
  body.panels-on .cover { padding-right: var(--rail-w); }
}

/* ── Panel nav ────────────────────────────────────────────────────────── */
.panel-nav {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
}
.panel-nav__item,
.panel-nav__print {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
  padding: 0.8rem 1rem;
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.panel-nav__item:first-child { border-top: 0; }
.panel-nav__num { font-variant-numeric: tabular-nums; opacity: 0.55; font-weight: 400; }
.panel-nav__item.is-active,
.panel-nav__item[aria-current="page"] {
  background: var(--ink);
  color: var(--paper);
}
.panel-nav__item.is-active .panel-nav__num { opacity: 0.75; }
.panel-nav__item:hover { background: var(--ink); color: var(--paper); }
.panel-nav__print { justify-content: center; gap: 0.5rem; margin-top: auto; }
.panel-nav__print:hover { background: var(--ink); color: var(--paper); }
.panel-nav__print .ph { font-size: 1rem; }

/* desktop: vertical rail, right edge */
@media (min-width: 1024px) {
  .panel-nav {
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    width: var(--rail-w);
    border: 1px solid var(--hairline);
    border-right: 0;
  }
  .panel-nav__item,
  .panel-nav__print { flex-direction: column; align-items: flex-start; }
  .panel-nav__item { padding: 1rem 1.25rem; }
  .panel-nav__label { font-weight: 400; }
}

/* mobile: bottom bar — labels stay VISIBLE (text-first navigation): the
   numbers are dropped and each label sits centered under the bar, so a
   guest always knows what 01–05 mean. Print keeps its icon + label. */
@media (max-width: 1023.98px) {
  .panel-nav {
    left: 0;
    right: 0;
    bottom: 0;
    flex-direction: row;
    justify-content: space-around;
    border-top: 1px solid var(--hairline);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .panel-nav__item {
    flex: 1;
    flex-direction: column;
    gap: 0.15rem;
    justify-content: center;
    border-top: 0;
    padding: 0.55rem 0.2rem 0.65rem;
  }
  .panel-nav__num { display: none; }
  .panel-nav__label {
    position: static;
    width: auto;
    height: auto;
    padding: 0;
    margin: 0;
    overflow: visible;
    clip: auto;
    white-space: nowrap;
    border: 0;
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-align: center;
  }
  .panel-nav__print {
    flex: 1;
    flex-direction: column;
    gap: 0.15rem;
    border-top: 0;
    border-left: 1px solid var(--hairline);
    margin-top: 0;
  }
  .panel-nav__print span {
    position: static;
    width: auto;
    height: auto;
    padding: 0;
    margin: 0;
    overflow: visible;
    clip: auto;
    white-space: nowrap;
    border: 0;
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }
  .panel-nav__print .ph { font-size: 0.95rem; line-height: 1; }
  @media (max-width: 430px) {
    .panel-nav__label, .panel-nav__print span { font-size: 0.5rem; letter-spacing: 0.04em; }
  }
  body.panels-on .panels > .panel { padding-bottom: calc(var(--pad-y) + var(--bottom-bar-h) + env(safe-area-inset-bottom)); }
  body.panels-on .panel--bleed { padding-bottom: 0 !important; }
  body.panels-on .cover { padding-bottom: calc(var(--bottom-bar-h) + env(safe-area-inset-bottom)); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   01 · THE COVER — the invitation card
   ═══════════════════════════════════════════════════════════════════════ */
.cover {
  position: relative;
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--chrome-h) + 2.5rem) var(--pad-x) 4rem;
  overflow: hidden;           /* clip the oversized parallax photo to the cover */
}

/* optional hero photo behind the card (placeholder in assets/img/). Shown
   in black & white to match the palette — delete `filter: grayscale(1)`
   to show it in colour. add data-hide to the <img> to remove it.
   Spans the full height of the cover (the cover is taller than the screen
   because the card scrolls), so the backdrop fills the entire screen and
   the card scrolls over it. The extra 15% height is the travel budget for
   the parallax drift (main.js moves it at a fraction of the panel scroll),
   so an edge is never exposed. */
.cover-photo {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  width: 100%;
  height: 115%;
  min-height: 100vh;
  min-height: 100dvh;
  object-fit: cover;
  object-position: center;
  filter: grayscale(1) contrast(1.05);
  will-change: transform;
}

/* the hero photo is clickable: the wrapped link fills the cover behind the
   card and opens the photo in the lightbox. hidden when the photo itself
   is data-hide. */
.cover-photo-link {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
}
.cover-photo-link:has(.cover-photo[data-hide]) { display: none; }
/* a quiet corner chip so guests know the backdrop is a photo they can view */
.cover-photo-hint {
  position: absolute;
  right: clamp(1rem, 4vw, 2rem);
  bottom: calc(1.5rem + env(safe-area-inset-bottom));
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.8rem;
  background: var(--paper);
  border: 1px solid var(--ink);
  color: var(--ink);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.85;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.cover-photo-hint-arrow { font-style: italic; }
.cover-photo-link:hover .cover-photo-hint,
.cover-photo-link:focus-visible .cover-photo-hint {
  background: var(--ink);
  color: var(--paper);
  opacity: 1;
}
@media (max-width: 1023.98px) {
  /* the bottom nav bar is fixed on mobile (see panel-nav below), so the
     hint must clear it — otherwise it would sit hidden behind the bar */
  .cover-photo-hint { bottom: calc(1rem + var(--bottom-bar-h) + env(safe-area-inset-bottom)); }
}

/* the card itself: a double hairline frame */
.card-frame {
  position: relative;
  z-index: 2;
  width: min(860px, 100%);
  border: 1px solid var(--ink);
  outline: 1px solid var(--ink);
  outline-offset: 0.9rem;
  padding: clamp(2.75rem, 8vw, 5.5rem) clamp(1.5rem, 6vw, 4.5rem) clamp(2rem, 6vw, 4rem);
  text-align: center;
  background: var(--paper);
}
/* paper halo behind the outer hairline: the photo only shows outside the
   frames, so the letterpress card keeps its clean signature look */
.card-frame::before {
  content: "";
  position: absolute;
  inset: -1.4rem;
  z-index: -1;
  background: var(--paper);
}

.cover__parents {
  opacity: 0.75;
  letter-spacing: 0.28em;
  margin-bottom: clamp(1.75rem, 4vw, 3rem);
}

.cover__names {
  font-size: clamp(3rem, 9.5vw, 7rem);
  line-height: 0.95;
  letter-spacing: -0.02em;
  font-weight: 700;
  text-transform: uppercase;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.cover__amp {
  font-style: italic;
  font-size: 0.32em;
  line-height: 1;
  margin: 0.18em 0 0.16em;
}

.cover__lead {
  max-width: 42ch;
  margin: clamp(1.5rem, 4vw, 2.5rem) auto 0;
  font-style: italic;
  font-size: clamp(0.95rem, 1.9vw, 1.15rem);
  line-height: 1.7;
  opacity: 0.9;
}

.cover__rule {
  display: block;
  width: min(240px, 70%);
  height: 1px;
  background: var(--ink);
  margin: clamp(1.75rem, 4.5vw, 3rem) auto;
  position: relative;
}
.cover__rule::after {
  content: "✕";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--paper);
  padding: 0 0.75rem;
  font-style: italic;
  font-size: 0.8rem;
  line-height: 1;
}

.cover__date {
  font-size: clamp(0.8rem, 1.6vw, 1rem);
  letter-spacing: 0.26em;
  text-indent: 0.26em;
}
.cover__time {
  margin-top: 1rem;
  opacity: 0.8;
  font-weight: 400;
  letter-spacing: 0.18em;
}

.cover__venue {
  font-size: clamp(1.1rem, 2.4vw, 1.6rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.cover__venue-sub {
  margin-top: 0.5rem;
  font-size: 0.85rem;
  opacity: 0.85;
}

.cover__tail { font-weight: 400; opacity: 0.9; }
.cover__rsvp-line { margin-top: 0.75rem; }

.cover__mark {
  margin-top: clamp(2.25rem, 5vw, 3.5rem);
  font-size: 0.6875rem;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  opacity: 0.55;
}

/* ═══════════════════════════════════════════════════════════════════════════
   02 · THE DETAILS
   ═══════════════════════════════════════════════════════════════════════ */
.details { max-width: 1080px; margin: 0 auto; }

.panel__index {
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
  opacity: 0.6;
  margin-bottom: clamp(1.75rem, 5vh, 3rem);
}
.panel__num { font-variant-numeric: tabular-nums; }

.details__title,
.rsvp-title,
.guestbook-title {
  font-size: clamp(2.1rem, 6.5vw, 4.5rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  font-weight: 700;
}

.details__lede {
  max-width: 52ch;
  margin-top: 1.25rem;
  font-size: clamp(1rem, 1.8vw, 1.2rem);
  opacity: 0.85;
}

.agenda { margin-top: clamp(2.5rem, 7vh, 4.5rem); border-top: 1px solid var(--ink); }
.agenda li {
  display: grid;
  grid-template-columns: 10rem 1fr auto;
  gap: 1.5rem;
  align-items: baseline;
  padding: 1.4rem 0;
  border-bottom: 1px solid var(--hairline);
  transition: background var(--t-fast) var(--ease), padding var(--t-fast) var(--ease);
}
.agenda li:hover { background: rgba(12, 12, 14, 0.045); padding-left: 0.75rem; padding-right: 0.75rem; }
.agenda__time { opacity: 0.6; }
.agenda__what { font-size: clamp(1.05rem, 2.4vw, 1.6rem); font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; }
.agenda__where { font-size: 0.85rem; opacity: 0.8; text-align: right; }

.notes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  margin-top: clamp(2.5rem, 7vh, 4rem);
}
.notes li {
  padding: 1.5rem clamp(1.25rem, 3vw, 2.5rem);
  border-bottom: 1px solid var(--hairline);
}
.notes li:nth-child(odd) { border-right: 1px solid var(--ink); }
.notes li:nth-last-child(-n + 2) { border-bottom: 0; }
.notes .k { display: block; opacity: 0.6; margin-bottom: 0.6rem; }
.notes .v { font-size: 1.05rem; line-height: 1.5; }

.directions__title {
  margin-top: clamp(2.5rem, 7vh, 4rem);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 0.9rem;
  letter-spacing: 0.2em;
}
.directions { margin-top: 0; }
.directions li {
  display: grid;
  grid-template-columns: 8rem 1fr;
  gap: 1.5rem;
  padding: 1.3rem 0;
  border-bottom: 1px solid var(--hairline);
}
.directions .k { opacity: 0.6; }
.directions .v { line-height: 1.5; }

.details__quote {
  margin-top: clamp(2.5rem, 7vh, 4rem);
  text-align: center;
  font-style: italic;
  font-size: clamp(1.05rem, 2.2vw, 1.5rem);
  opacity: 0.9;
}

/* ═══════════════════════════════════════════════════════════════════════════
   03 · OUR MOMENTS — the photo gallery
   Optional panel: every .moment-img ships with a placeholder in
   assets/img/. add data-hide to the <section> to turn the whole gallery
   off (main.js drops it from the rail too); data-hide on a single
   <figure> hides just that frame. Photos render in black & white to
   match the palette — delete `filter: grayscale(1)` on .moment-img to
   show them in colour.
   ═══════════════════════════════════════════════════════════════════════ */
.gallery { max-width: 1080px; margin: 0 auto; }
.gallery-title { margin-bottom: 1.25rem; }
.gallery-description { opacity: 0.8; max-width: 52ch; }

/* optional banner / save-the-date frame (above the grid). Same lightbox +
   placeholder behaviour as the moments; add data-hide to the <figure> to
   turn it off. Wide poster ratio on desktop, gentler on phones. */
.banner-frame { margin-bottom: clamp(2.5rem, 7vh, 4rem); }
.banner-frame .moment-img { aspect-ratio: 21 / 9; }
@media (max-width: 760px) {
  .banner-frame .moment-img { aspect-ratio: 16 / 9; }
}

.moments-grid {
  margin-top: clamp(2.5rem, 7vh, 4rem);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.25rem, 3vw, 2.25rem);
}
/* the first frame (the couple) spans the full width as a hero */
.moment:first-child { grid-column: 1 / -1; }

.moment {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 0.65rem 0.65rem 0.9rem;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.moment-link { display: block; }
.moment-img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  filter: grayscale(1) contrast(1.02);
}
/* the hero frame (the couple) is a wider landscape so the subjects sit
   in the first screen without scrolling */
.moment:first-child .moment-img { aspect-ratio: 16 / 9; }
.moment-caption {
  margin-top: 0.8rem;
  padding: 0 0.25rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  font-weight: 700;
  opacity: 0.8;
}
/* a hard, un-blurred lift — very mono */
.moment:hover { transform: translateY(-3px); box-shadow: 6px 6px 0 rgba(12, 12, 14, 0.16); }
.moment:focus-within { box-shadow: 6px 6px 0 rgba(12, 12, 14, 0.16); }

/* ═══════════════════════════════════════════════════════════════════════════
   GALLERY · OUR FILM — optional video feature
   Paste a video URL into .film-card[data-video-url] (YouTube / Vimeo / any
   embeddable URL). Tapping play lazy-loads the iframe — nothing loads until
   a guest taps. Empty URL → gentle note. Optional: data-hide on the
   <section> (or delete it) to hide the feature. Photos render in black &
   white to match the palette.
   ═══════════════════════════════════════════════════════════════════════ */
.film-card[data-hide] { display: none !important; }
.film-card {
  margin-top: clamp(3rem, 8vh, 4.5rem);
  border-top: 1px solid var(--ink);
  padding-top: clamp(2rem, 6vh, 3rem);
}
.film-title {
  font-size: clamp(1.6rem, 4.5vw, 2.6rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  font-weight: 700;
}
.film-description {
  margin-top: 0.9rem;
  max-width: 52ch;
  opacity: 0.8;
}
.film-frame {
  position: relative;
  width: 100%;
  max-width: 860px;
  margin: clamp(1.75rem, 5vh, 2.75rem) 0 0;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--paper);
  border: 1px solid var(--ink);
}
.film-open {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.film-open img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.02);
}
.film-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(3.5rem, 10vmin, 4.75rem);
  height: clamp(3.5rem, 10vmin, 4.75rem);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  border: 1px solid var(--ink);
  box-shadow: 6px 6px 0 rgba(12, 12, 14, 0.16);
  font-size: 1rem;
  line-height: 1;
  padding-left: 0.15em;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.film-open:hover .film-play { transform: translate(-50%, -50%) scale(1.06); box-shadow: 8px 8px 0 rgba(12, 12, 14, 0.2); }
.film-hint {
  position: absolute;
  left: 50%;
  bottom: 0.9rem;
  transform: translateX(-50%);
  padding: 0.4rem 0.9rem;
  background: var(--ink);
  color: var(--paper);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  pointer-events: none;
  white-space: nowrap;
}
.film-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.film-note {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0 1.5rem;
  text-align: center;
  font-style: italic;
  background: var(--paper);
  cursor: pointer;
  opacity: 0.9;
}
.film-external {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 1.25rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 0.2rem;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.film-external:hover { background: var(--ink); color: var(--paper); }
.film-external[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   FREE PAGE — optional “In Our Words” panel (host-filled)
   One repeatable block per entry: title + free text + OPTIONAL photo. The
   panel ships hidden — the render engine removes the whole section when no
   blocks exist (or when the host toggles the Free Page section off), so the
   nav entry that main.js syncs from this panel disappears with it. A block
   without a photo (engine removes .free-media) still renders cleanly.
   ═══════════════════════════════════════════════════════════════════════ */
.free-page { max-width: 780px; margin: 0 auto; }

.free-blocks { margin-top: clamp(1.5rem, 4vh, 2.5rem); }
.free-block {
  text-align: center;
  padding: clamp(2.25rem, 6vh, 3.5rem) 0;
  border-top: 1px solid var(--hairline);
}
.free-block:first-child { border-top: 0; padding-top: 0; }
.free-title {
  font-size: clamp(1.9rem, 6vw, 3.2rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  font-weight: 700;
}
.free-title:focus { outline: none; }

/* optional photo — framed like a gallery moment, mono and clickable */
.free-media {
  margin: clamp(1.5rem, 4vh, 2.5rem) auto;
  max-width: 560px;
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 0.65rem 0.65rem 0.9rem;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.free-media:hover { transform: translateY(-3px); box-shadow: 6px 6px 0 rgba(12, 12, 14, 0.16); }
.free-media .moment-link { display: block; }
/* A free block without a photo: the engine removes the <img> only, so hide
   the empty framed figure (and its dead anchor) instead of showing an empty
   box. Guarded in @supports so pre-:has browsers keep today's behaviour;
   main.js also drops these figures at boot. */
@supports selector(:has(img)) {
  .free-media:not(:has(img)) { display: none; }
}
.free-img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  filter: grayscale(1) contrast(1.02);
}
.free-content {
  margin: 0 auto;
  max-width: 52ch;
  text-align: left;
  font-size: 1rem;
  line-height: 1.75;
  opacity: 0.85;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* ═══════════════════════════════════════════════════════════════════════════
   04 · RSVP (form is ours; behaviour is the platform's)
   ═══════════════════════════════════════════════════════════════════════ */
.rsvp-wrap { max-width: 780px; margin: 0 auto; }
/* The wrapper class also anchors the engine's RSVP toggle-strip on the
   panel section itself (index.html) — keep the section full-width; the
   780px cap belongs to the inner content. */
.panel--rsvp.rsvp-wrap { max-width: none; margin: 0; }
.rsvp-title { margin-bottom: 1.25rem; }
.rsvp-description { opacity: 0.8; max-width: 52ch; }

.guest-greeting {
  margin-top: 1.5rem;
  font-size: 1.05rem;
  font-weight: 700;
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  padding: 0.9rem 0;
  letter-spacing: 0.04em;
}

/* countdown (revealed by main.js only when a deadline exists) */
.rsvp-countdown {
  margin-top: 2.5rem;
  border: 1px solid var(--ink);
  padding: 1.5rem;
}
.rsvp-countdown__label { opacity: 0.6; margin-bottom: 1rem; }
.rsvp-countdown__digits { display: flex; gap: clamp(1rem, 4vw, 3rem); }
.rsvp-countdown__cell { display: flex; flex-direction: column; }
.rsvp-countdown__cell span {
  font-size: clamp(1.9rem, 5vw, 3.2rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.rsvp-countdown__cell small { opacity: 0.6; letter-spacing: 0.16em; text-transform: uppercase; font-size: 0.625rem; margin-top: 0.5rem; }

/* the form */
#rsvp-form { margin-top: 2.5rem; }

.rsvp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 2.25rem; }
.rsvp-field { margin-bottom: 2.25rem; }
.rsvp-field--wide { grid-column: 1 / -1; }

.rsvp-field label {
  display: block;
  margin-bottom: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.6875rem;
}

.f-control {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--ink);
  border-radius: 0;
  background: transparent;
  padding: 0.65rem 0.4rem;
  font-size: 1rem;
  color: var(--ink);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.f-control::placeholder { color: var(--muted); opacity: 0.75; }
.f-control:focus {
  outline: none;
  background: var(--ink);
  color: var(--paper);
}
.f-control:focus::placeholder { color: rgba(253, 253, 252, 0.55); }

textarea.f-control {
  resize: vertical;
  min-height: 4.5rem;
  line-height: 1.6;
}

/* selects: custom arrow, inverts with the field */
.f-select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%230c0c0e' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.25rem center;
  padding-right: 2rem;
}
.f-select:focus {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23fdfdfc' stroke-width='1.5'/%3E%3C/svg%3E");
}
.f-select option { color: var(--ink); background: var(--paper); }

/* platform inline errors (hidden until the platform writes text) */
.error {
  display: none;
  margin-top: 0.55rem;
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  background: var(--ink);
  color: var(--paper);
  padding: 0.35rem 0.65rem;
  line-height: 1.4;
}
.error:not(:empty) { display: block; }
.error::before { content: "! "; font-weight: 700; }

/* submit */
.rsvp-submit {
  display: block;
  width: 100%;
  background: var(--ink);
  color: var(--paper);
  border: 1px solid var(--ink);
  padding: 1.05rem 1.5rem;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.rsvp-submit:hover { background: var(--paper); color: var(--ink); }
.rsvp-submit:disabled { opacity: 0.45; cursor: default; }
.rsvp-submit:focus-visible { outline-offset: 3px; }

/* loading (shown/hidden by the platform — jQuery .show() sets an inline
   style that overrides this default) */
.loading {
  display: none;
  margin-top: 1.25rem;
  text-align: center;
  letter-spacing: 0.2em;
  opacity: 0.75;
}
.loading::before {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-right: 0.6em;
  vertical-align: -0.15em;
  border: 1px solid var(--ink);
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* platform thank-you / closed state (injected into #rsvp-form) */
#rsvp-form .thank-you-message {
  border: 1px dashed var(--ink);
  padding: clamp(2rem, 6vh, 3.5rem) 1.5rem !important;
  text-align: center;
  margin-top: 1rem !important;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}
#rsvp-form .thank-you-message h3 {
  font-size: clamp(1.5rem, 4vw, 2.4rem);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  font-weight: 700;
}
#rsvp-form .thank-you-message p { opacity: 0.85; max-width: 46ch; }
#rsvp-form .thank-you-message small { opacity: 0.6; }

/* ═══════════════════════════════════════════════════════════════════════════
   05 · GUESTBOOK (Style A mount — platform injects the form + list)
   ═══════════════════════════════════════════════════════════════════════ */
.guestbook-section { max-width: 780px; margin: 0 auto; }
/* Same as RSVP: the section itself carries .guestbook-section so the engine
   strips the whole panel when the guestbook is off; keep it full-width. */
.panel--guestbook.guestbook-section { max-width: none; margin: 0; }
.guestbook-title { margin-bottom: 1.25rem; }
.guestbook-description { opacity: 0.8; max-width: 52ch; }

/* injected functional block */
.guestbook-form { margin-top: 2.5rem; }
#guestbookForm .form-group { margin-bottom: 2.25rem; }
#guestbookForm label {
  display: block;
  margin-bottom: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.6875rem;
}
#guestbookForm .form-control {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--ink);
  border-radius: 0;
  background: transparent;
  padding: 0.65rem 0.4rem;
  font-size: 1rem;
  color: var(--ink);
  font-family: var(--font-mono);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
#guestbookForm textarea.form-control { resize: vertical; min-height: 5.5rem; line-height: 1.6; }
#guestbookForm .form-control:focus { outline: none; background: var(--ink); color: var(--paper); }
#guestbookForm .error {
  display: none;
  margin-top: 0.55rem;
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  background: var(--ink);
  color: var(--paper);
  padding: 0.35rem 0.65rem;
}
#guestbookForm .error:not(:empty) { display: block; }
#guestbookForm .error::before { content: "! "; font-weight: 700; }

.guestbook-chapta { margin: 1rem 0 1.75rem; }

/* re-skin the platform's Bootstrap-ish button to match */
.guestbook-section .d-flex { display: flex; }
.guestbook-section .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  background: var(--ink) !important;
  color: var(--paper) !important;
  border: 1px solid var(--ink);
  border-radius: 0 !important;
  padding: 0.9rem 1.75rem !important;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  cursor: pointer;
  margin: 0 auto !important;
  margin-top: 0.5rem !important;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.guestbook-section .btn:hover { background: var(--paper) !important; color: var(--ink) !important; }
.guestbook-section .btn:disabled { opacity: 0.45; cursor: default; }
.guestbook-section .btn .ph { font-size: 1.05rem; }

.loading-guestbook {
  margin-top: 1.25rem;
  text-align: center;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  opacity: 0.75;
}

/* message list (overrides the injected inline styles) */
#comments-container {
  max-height: none !important;
  overflow: visible !important;
  border-top: 1px solid var(--ink) !important;
  margin-top: 2.75rem !important;
  padding-top: 0.5rem;
}
.guestbook-entry {
  padding: 1.4rem 0;
  border-bottom: 1px solid var(--hairline);
  overflow-wrap: anywhere;
}
.guestbook-entry h5 {
  margin: 0 0 0.4rem;
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.guestbook-entry p { margin: 0 0 0.4rem; font-size: 0.9375rem; line-height: 1.65; }
.guestbook-entry small { font-size: 0.6875rem; opacity: 0.6; letter-spacing: 0.08em; }

.more-guestbook-wrap { margin-top: 1.75rem; text-align: center; }
#load-more {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--ink);
  padding: 0.75rem 1.75rem;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
#load-more:hover { background: var(--ink); color: var(--paper); }

/* guestbook thank-you (injected) */
.guestbook-section .thank-you-message {
  border: 1px dashed var(--ink);
  padding: 2rem 1.5rem;
  text-align: center;
}
.guestbook-section .thank-you-message h4 {
  font-size: 1.4rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-weight: 700;
  margin-bottom: 0.5rem;
}
.guestbook-section .thank-you-message p { opacity: 0.85; }

/* ═══════════════════════════════════════════════════════════════════════════
   PHOTO LIGHTBOX — full-size viewer for the gallery
   Paper wall with a double-hairline stage; the ✕ sits top-right. Opened
   by main.js when a .moment-link is clicked; without JS the anchor still
   opens the photo in a new tab. The rest of the shell goes inert while
   the lightbox is open.
   ═══════════════════════════════════════════════════════════════════════ */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: var(--paper);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-slow) var(--ease), visibility 0s linear var(--t-slow);
}
.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--t-slow) var(--ease);
}
.lightbox-stage {
  position: relative;
  width: min(92vw, 920px);
  max-height: 100%;
  background: var(--paper);
  border: 1px solid var(--ink);
  outline: 1px solid var(--ink);
  outline-offset: 0.5rem;
  padding: 1rem;
  overflow: hidden;
}
.lightbox.is-zoomed .lightbox-stage { overflow: auto; cursor: grab; }
.lightbox.is-zoomed .lightbox-stage:active { cursor: grabbing; }
.lightbox-img {
  display: block;
  width: 100%;
  max-height: 70vh;
  object-fit: contain;
  filter: grayscale(1) contrast(1.02);
  cursor: zoom-in;
  transition: width var(--t-slow) var(--ease);
}
/* zoomed: the photo doubles and the stage scrolls, so guests can pan by
   dragging (or arrow keys / trackpad). Click again to reset. */
.lightbox.is-zoomed .lightbox-img {
  width: 200%;
  max-height: none;
  cursor: zoom-out;
}
.lightbox-caption {
  margin-top: 1.1rem;
  text-align: center;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  font-weight: 700;
  opacity: 0.8;
  min-height: 1.2em;
}
.lightbox-hint {
  margin-top: 0.5rem;
  text-align: center;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 0.5625rem;
  font-weight: 400;
  opacity: 0.5;
}
.lightbox-close,
.lightbox-zoom {
  position: fixed;
  top: 1rem;
  z-index: 1;
  width: 2.75rem;
  height: 2.75rem;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.lightbox-close { right: 1rem; }
.lightbox-zoom { right: 4rem; font-size: 1rem; }
.lightbox-close:hover,
.lightbox-zoom:hover { background: var(--paper); color: var(--ink); }
@media (max-width: 480px) {
  .lightbox-zoom { right: 3.5rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   FOOTER — brand placeholder
   .app-footer > .footer-brand after </main>. No-JS: plain closing line at
   the end of the stacked document. JS shell: initFooter() tucks it into
   the last existing panel, so it survives RSVP/Guestbook being stripped.
   ═══════════════════════════════════════════════════════════════════════ */
.app-footer { padding: clamp(2.5rem, 7vh, 4rem) 0 1rem; text-align: center; }
.footer-brand {
  margin: 0;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  opacity: 0.55;
}
.footer-brand .cover__amp { font-style: italic; opacity: 0.8; }
body.panels-on .app-footer {
  border-top: 1px solid var(--hairline);
  margin-top: clamp(3rem, 8vh, 5rem);
  padding-bottom: 0.5rem;
}

/* ── powered-by brand strip (full-width footer line) ───────────────────
   A slim “Powered by Kundangi” line at the very foot of the invitation.
   The engine swaps the literal Kundangi token for the platform name at
   render; the cockpit's Free Powered By field can override the whole
   line. Full-width in the JS shell (tucked into the last panel by
   main.js, after the brand footer) and at the end of the stacked
   document without JS. */
.powered-by {
  width: 100%;
  text-align: center;
  margin-top: clamp(2rem, 6vh, 3.5rem);
  padding: 1rem 0 0.25rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0.7;
}
.powered-by .link-footer { color: var(--ink); font-weight: 700; }

/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {
  /* The paper moat behind the double hairline must not swallow the hero
     photo on phones — the card + halo spanned the full viewport width, so
     the backdrop was invisible. Shrink the moat to match the tighter
     outline offset and keep the card off the screen edges so the photo
     frames it on every screen. */
  .card-frame { outline-offset: 0.5rem; }
  .card-frame::before { inset: -0.5rem; }
  .cover {
    padding-left: clamp(1.25rem, 7vw, 2.5rem);
    padding-right: clamp(1.25rem, 7vw, 2.5rem);
  }

  .agenda li { grid-template-columns: 1fr; gap: 0.35rem; padding: 1.2rem 0; }
  .agenda__where { text-align: left; }

  .notes { grid-template-columns: 1fr; }
  .notes li:nth-child(odd) { border-right: 0; }
  .notes li:nth-last-child(-n + 2) { border-bottom: 1px solid var(--hairline); }
  .notes li:last-child { border-bottom: 0; }

  .directions li { grid-template-columns: 1fr; gap: 0.35rem; }

  .moments-grid { grid-template-columns: 1fr; }

  .rsvp-grid { grid-template-columns: 1fr; }
}

@media (max-width: 480px) {
  .chrome__sub { display: none; }
  .cover__parents { letter-spacing: 0.2em; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   REDUCED MOTION
   ═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
  body.panels-on .panel { transition: none; }
  .moment:hover { transform: none; }
  .skip-link { transform: translateY(-300%); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PRINT — the invitation card + details as clean sheets; interactive parts
   (nav, chrome, RSVP, guestbook) are omitted.
   ═══════════════════════════════════════════════════════════════════════ */
@media print {
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  body { background: #fff !important; }

  .chrome, .panel-nav, .skip-link { display: none !important; }
  .cover-photo, .cover-photo-link, .panel--rsvp, .panel--gallery, .panel--guestbook,
  .app-footer, .powered-by, .lightbox { display: none !important; }

  body.panels-on .shell { height: auto !important; overflow: visible !important; }
  body.panels-on .panels { position: static !important; }
  body.panels-on .panel {
    position: static !important;
    inset: auto !important;
    height: auto !important;
    overflow: visible !important;
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
    padding: 0.5in !important;
    page-break-after: always;
  }
  body.panels-on .panel:last-of-type { page-break-after: auto; }

  .cover { min-height: auto !important; padding: 0.25in !important; }
  .card-frame {
    outline-offset: 0.35rem;
    padding: 1.25in 0.75in;
    width: 100%;
  }
  .panel__index { margin-bottom: 0.75rem; }
}
