/* ═══════════════════════════════════════════════════════════════════════
   wedding-floral-watercolor-card-rose-garden — styles.css
   "Rose Garden" — a romantic botanical wedding invitation card.

   Palette (soft watercolor florals, delicate feminine):
     ivory paper  #FBF7F0 · blush #F2D8CF · dusty rose #D9968B / #C98A7F
     sage #A9BA9B / #7C8F74 · mauve #C4AEC6 · warm taupe ink #5B4E49

   Layout: one 100dvh shell — masthead / tabs / active panel / foot.
   Only the active panel scrolls (html.js gates this; without JS the page
   reads as a normal scrolling document).
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* palette */
  --paper: #FBF7F0;
  --paper-deep: #F4EDE0;
  --card: #FFFDF8;
  --blush: #F2D8CF;
  --rose: #D9968B;
  --rose-deep: #C07B72;
  --rose-ink: #A96A60;
  --mauve: #C4AEC6;
  --sage: #A9BA9B;
  --sage-deep: #7C8F74;
  --green-ink: #46604B;
  --ink: #5B4E49;
  --muted: #8D7E78;
  --line: rgba(91, 78, 73, 0.16);

  /* type */
  --font-script: "Great Vibes", "Brush Script MT", cursive;
  --font-display: "Cormorant Garamond", "Times New Roman", serif;
  --font-ui: "Montserrat", "Helvetica Neue", Arial, sans-serif;

  /* shape */
  --radius: 18px;
  --shadow-soft: 0 14px 40px rgba(120, 92, 78, 0.16);
  --shadow-card: 0 30px 80px rgba(120, 92, 78, 0.24);
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

html { height: 100%; }

body {
  height: 100%;
  background:
    radial-gradient(120% 90% at 50% 0%, #FFFDF8 0%, var(--paper) 55%, #F3EBDD 100%);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 16.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* faint paper grain so the washes feel like real paper */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 5;
  opacity: 0.5;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
}

/* ── the shell ─────────────────────────────────────────────────────────── */
/* JS mode: one 100dvh app with a fixed chrome and a single scrolling
   panel. Without JS this collapses to a normal scrolling document and
   every panel reads in order (progressive enhancement). */
html.js .app {
  height: 100vh;
  height: 100dvh;
  display: grid;
  /* One column sized by the viewport, not by content min-width: without
     this, the nowrap masthead name / wide tab row stretch the implicit
     `auto` track past the screen on narrow phones and the shell clips the
     overflow instead of reflowing. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  overflow: hidden;
}

html:not(.js) .app { height: auto; overflow: visible; display: block; }
html:not(.js) .panels { height: auto; overflow: visible; }
html:not(.js) .panel { display: block; position: static; overflow: visible; }
html:not(.js) .tabs { display: none; }

/* ── masthead ─────────────────────────────────────────────────────────── */
.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px clamp(14px, 3vw, 28px);
  padding-top: calc(10px + env(safe-area-inset-top));
  background: rgba(251, 247, 240, 0.82);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid rgba(91, 78, 73, 0.08);
}

.masthead__brand { display: flex; align-items: center; gap: 11px; min-width: 0; }

.masthead__mark {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, #FFF6F1, var(--blush));
  box-shadow: inset 0 0 0 1px rgba(201, 138, 127, 0.35), 0 4px 12px rgba(201, 138, 127, 0.25);
}

.masthead__text { min-width: 0; line-height: 1.25; }
.masthead__top {
  display: block;
  font: 500 9.5px var(--font-ui);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sage-deep);
}
.masthead__name {
  display: block;
  font-family: var(--font-script);
  font-size: 21px;
  color: var(--rose-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.masthead__name i { font-style: normal; color: var(--sage-deep); padding: 0 2px; }

.masthead__actions { display: flex; gap: 8px; }

.chip-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(255, 253, 248, 0.85);
  color: var(--ink);
  font: 500 13px var(--font-ui);
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s, background 0.2s, transform 0.15s;
}
.chip-btn:hover { border-color: var(--rose); color: var(--rose-deep); }
.chip-btn:active { transform: translateY(1px); }
.chip-btn.is-done { border-color: var(--sage-deep); color: var(--sage-deep); }

/* ── tabs ──────────────────────────────────────────────────────────────── */
.tabs {
  display: flex;
  gap: 4px;
  padding: 6px clamp(10px, 2.5vw, 24px) 8px;
  background: rgba(251, 247, 240, 0.75);
  border-bottom: 1px solid rgba(91, 78, 73, 0.08);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  appearance: none;
  border: none;
  background: transparent;
  padding: 8px 16px;
  border-radius: 999px;
  font: 500 12px var(--font-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.2s, background 0.2s;
}
.tab:hover { color: var(--rose-deep); }
.tab[aria-selected="true"] {
  color: #fff;
  background: linear-gradient(135deg, var(--rose) 0%, var(--rose-deep) 100%);
  box-shadow: 0 6px 16px rgba(201, 138, 127, 0.4);
}

/* ── panels ────────────────────────────────────────────────────────────── */
.panels {
  position: relative;
  min-height: 0;
  overflow: hidden;
}

html.js .panel {
  display: none;
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
}
html.js .panel.is-active { display: block; }
.panel > * { scroll-margin-top: 18px; }

/* content column for tab panels (the card uses the full panel) */
.panel-inner {
  max-width: 640px;
  margin: 0 auto;
  padding: clamp(22px, 4vw, 44px) clamp(16px, 4vw, 28px) clamp(36px, 6vw, 64px);
}

/* ── hero: the stationery card ─────────────────────────────────────────── */
.hero-panel {
  position: relative;
  background:
    radial-gradient(90% 70% at 50% 42%, rgba(255, 253, 248, 0.9) 0%, rgba(255, 253, 248, 0.55) 45%, transparent 78%),
    radial-gradient(120% 90% at 50% 0%, #FFFDF8 0%, var(--paper) 55%, #F3EBDD 100%);
}

.card-scene {
  position: relative;
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(60px, 9vh, 88px) clamp(24px, 6vw, 44px) clamp(60px, 9vh, 88px);
}

/* the invitation sheet */
.card {
  position: relative;
  width: min(560px, 100%);
  background: linear-gradient(180deg, #FFFEFB 0%, var(--card) 100%);
  border-radius: 4px;
  box-shadow: var(--shadow-card);
  padding: clamp(52px, 9vh, 78px) clamp(30px, 6vw, 58px) clamp(46px, 8vh, 66px);
  text-align: center;
}

/* hairline double frame around the sheet */
.card__frame {
  position: absolute;
  inset: 12px;
  border: 1px solid rgba(169, 106, 96, 0.28);
  border-radius: 2px;
  pointer-events: none;
}
.card__frame::after {
  content: "";
  position: absolute;
  inset: 4px;
  border: 1px solid rgba(124, 143, 116, 0.2);
  border-radius: 2px;
}

/* corner + top florals, framed to the sheet (not the screen) */
.florals {
  position: absolute;
  pointer-events: none;
  z-index: 1;
}
.card .florals--tl {
  top: -16px;
  left: -20px;
  width: clamp(120px, 24vw, 196px);
  height: auto;
}
.card .florals--br {
  bottom: -16px;
  right: -20px;
  width: clamp(120px, 24vw, 196px);
  height: auto;
}
.card .florals--top {
  top: -10px;
  left: 50%;
  width: min(280px, 52vw);
  height: auto;
  transform: translateX(-50%);
  opacity: 0.9;
}

.card__greeting {
  margin: 0 0 20px;
  font: 500 13px var(--font-ui);
  letter-spacing: 0.04em;
  color: var(--rose-deep);
}

.card__hosts {
  margin: 0;
  font: 500 11.5px var(--font-ui);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--sage-deep);
}

.card__names {
  margin: 18px 0 0;
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(52px, 9vw, 76px);
  line-height: 1.05;
  color: var(--rose-deep);
  text-wrap: balance;
}
.card__amp {
  font-style: normal;
  color: var(--sage-deep);
  padding: 0 0.16em;
  font-size: 0.68em;
  vertical-align: 0.12em;
}

.card__request {
  margin: 10px 0 0;
  font: italic 500 clamp(17px, 2.8vw, 20px) var(--font-display);
  color: var(--ink);
  line-height: 1.5;
}

.card__divider { margin: 18px 0 12px; }

.card__date {
  margin: 0;
  font: 500 clamp(17px, 2.8vw, 20px) var(--font-display);
  color: var(--green-ink);
  line-height: 1.4;
}

.card__place {
  margin: 14px 0 0;
  font-size: clamp(15px, 2.4vw, 16.5px);
  color: var(--ink);
  line-height: 1.6;
}
.card__place strong { font-weight: 600; color: var(--rose-ink); }
.card__place span { font-style: italic; color: var(--muted); font-size: 0.94em; }

.card__dress {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 0;
}
.card__dress span {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(124, 143, 116, 0.5), transparent);
}
.card__dress p {
  margin: 0;
  font: 500 11px var(--font-ui);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted);
}

.card__reply {
  margin: 16px 0 0;
  font: italic 500 15px var(--font-display);
  color: var(--muted);
  line-height: 1.5;
}

/* Optional card lines must degrade to NOTHING, never a gap or an artifact.
   The engine's hidden_if_empty removes only the inner span/p text node, so
   when a host leaves the Dress or Reply line blank an empty wrapper stays:
   .card__reply keeps its 16px top margin (a stray gap at the card bottom)
   and .card__dress keeps its two bare hairline spans (a double-rule mark
   with nothing between). Collapse both — the card reads clean either way. */
.card__reply:empty,
.card__dress:not(:has(p)) {
  display: none;
}

/* ── optional couple cover: a large arched portrait at the top of the
      sheet, the way a printed invitation opens with a photo. The arch and
      cream mat echo the sheet's hairline double frame, and the corner
      florals paint over its edges. Delete the <figure> to hide it. ── */
.card__cover {
  position: relative;
  z-index: 0;
  width: 100%;
  max-width: 400px;
  margin: 0 auto clamp(26px, 4.5vh, 40px);
  padding: 8px;
  border-radius: 999px 999px 7px 7px;
  background: linear-gradient(180deg, #FFFEFB 0%, var(--card) 100%);
  border: 1px solid rgba(201, 138, 127, 0.45);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.7),
    0 0 0 5px rgba(124, 143, 116, 0.3),
    0 20px 38px -18px rgba(120, 92, 78, 0.4);
}
.card__cover-link { display: block; text-decoration: none; }
.card__cover-link:focus-visible { outline: 2px solid var(--rose); outline-offset: 2px; }
.card__cover img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  border-radius: 999px 999px 4px 4px;
  object-fit: cover;
  background: var(--paper-deep); /* soft tone while loading / if the file is missing */
}

/* ── section head ──────────────────────────────────────────────────────── */
.section-head { text-align: center; margin-bottom: 30px; }
.section-head .kicker {
  margin: 0 0 8px;
  font: 500 11.5px var(--font-ui);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--sage-deep);
}
.section-head h2 {
  margin: 0;
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(38px, 6vw, 52px);
  line-height: 1.1;
  color: var(--rose-deep);
  text-wrap: balance;
}
.section-head .lede {
  margin: 12px auto 0;
  max-width: 46em;
  font-size: 17px;
  color: var(--muted);
  line-height: 1.6;
}
.section-head .lede strong { color: var(--rose-ink); font-weight: 600; }

/* ── countdown tiles (self-driven; never .count-*) ────────────────────── */
.countdown-tiles { text-align: center; margin-bottom: 34px; }
.countdown-tiles__label {
  margin: 0 0 12px;
  font: 500 11px var(--font-ui);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted);
}
.tiles {
  display: flex;
  justify-content: center;
  gap: clamp(8px, 2vw, 14px);
}
.tile {
  min-width: clamp(58px, 14vw, 76px);
  padding: 14px 6px 11px;
  border-radius: 10px;
  border: 1px solid rgba(169, 106, 96, 0.26);
  background: var(--card);
  /* inner sage "mat" turns the tile into a framed number card, kin to the
     invitation sheet's hairline double frame — no gradient fill. */
  box-shadow: 0 8px 22px rgba(120, 92, 78, 0.1), inset 0 0 0 3px rgba(124, 143, 116, 0.12);
}
.tile-num {
  display: block;
  font: 600 clamp(22px, 4.6vw, 30px) var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--rose-ink);
}
.tile-cap {
  display: block;
  font: 500 9.5px var(--font-ui);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 2px;
}
.countdown-tiles__here {
  margin: 14px 0 0;
  font: italic 500 17px var(--font-display);
  color: var(--rose-ink);
}

/* ── block titles / timeline ───────────────────────────────────────────── */
.block-title {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 18px;
  font: 500 12px var(--font-ui);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--sage-deep);
}
.block-title::before,
.block-title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(124, 143, 116, 0.5), transparent);
}

.timeline { list-style: none; margin: 0 0 34px; padding: 0; }
.timeline-item {
  display: flex;
  gap: 18px;
  align-items: baseline;
  padding: 13px 4px 13px 0;
  border-bottom: 1px dashed rgba(124, 143, 116, 0.35);
}
.timeline-item:last-child { border-bottom: none; }
.timeline-item__time {
  flex: none;
  width: 82px;
  font: 600 15.5px var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--rose-ink);
}
.timeline-item__text { font-size: 17px; color: var(--ink); }

/* ── venues ────────────────────────────────────────────────────────────── */
.event-list { display: grid; gap: 14px; margin-bottom: 30px; }
.event-item {
  position: relative;
  padding: 22px 22px 20px;
  /* flatter radius reads as cut stationery, not a bubbled card */
  border-radius: 8px;
  border: 1px solid rgba(169, 106, 96, 0.24);
  /* soft watercolor corners (blush + sage) echo the invitation's florals;
     flat ivory base instead of a solid colour block. */
  background:
    radial-gradient(140px 140px at -10% -15%, rgba(242, 216, 207, 0.6), transparent 72%),
    radial-gradient(120px 120px at 110% 0%, rgba(169, 186, 155, 0.34), transparent 72%),
    var(--card);
  /* inner sage mat = the double-frame motif of the invitation card */
  box-shadow: var(--shadow-soft), inset 0 0 0 4px rgba(124, 143, 116, 0.1);
}
.event-item__chip {
  display: inline-block;
  padding: 3px 11px;
  border-radius: 999px;
  background: var(--blush);
  color: var(--rose-ink);
  font: 600 10.5px var(--font-ui);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.event-item__title { margin: 10px 0 4px; font: 600 22px var(--font-display); color: var(--green-ink); }
.event-item__when { margin: 0; font: 500 13px var(--font-ui); color: var(--rose-deep); }
.event-item__addr { margin: 6px 0 0; font-size: 15.5px; color: var(--muted); line-height: 1.5; }
.event-item__actions { margin-top: 12px; }

/* ── notes ─────────────────────────────────────────────────────────────── */
.notes {
  display: grid;
  gap: 10px;
  padding: 18px 20px;
  border-radius: var(--radius);
  border: 1px dashed rgba(124, 143, 116, 0.55);
  background: rgba(169, 186, 155, 0.12);
}
.note { margin: 0; font-size: 15.5px; color: var(--ink); line-height: 1.55; }
.note strong { color: var(--sage-deep); font-weight: 600; }

/* ── Gallery: "Moments to keep" (optional .story-gallery block) ──────────
   Same class names as the repo's other photo galleries — replace any img
   src with your own photo, or delete the block to hide the feature. */
.story-gallery { margin-top: 6px; }
.gallery-label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 0 0 18px;
  font: 500 11px var(--font-ui);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--sage-deep);
}
.gallery-label::before,
.gallery-label::after {
  content: "";
  flex: 1;
  max-width: 90px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(124, 143, 116, 0.5), transparent);
}
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(12px, 2.6vw, 18px);
  align-items: start;
  grid-auto-flow: dense;
}
.gallery-item { position: relative; margin: 0; }
/* The gallery is a repeatable group: the engine clones the first .gallery-item
   per photo row, so the featured span is a :first-child grid rule (it must
   survive cloning — a class modifier would be duplicated onto every clone). */
.gallery-grid > .gallery-item:first-child { grid-column: span 2; }
.gallery-item img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  padding: 5px;
  background: var(--card);
  border: 1px solid rgba(169, 106, 96, 0.3);
  border-radius: 4px;
  /* inner sage mat = the double-frame motif of the invitation sheet */
  box-shadow: 0 12px 26px -18px rgba(120, 92, 78, 0.5), inset 0 0 0 3px rgba(124, 143, 116, 0.12);
}
.gallery-grid > .gallery-item:first-child img { aspect-ratio: 3 / 2; }
.gallery-item figcaption {
  margin-top: 8px;
  font-family: var(--font-script);
  font-size: clamp(19px, 3vw, 22px);
  color: var(--rose-ink);
  text-align: center;
}

/* keepsake card below the grid (share under the hashtag). It sits OUTSIDE
   .gallery-grid (the repeatance engine rewrites that container's innertext),
   so it is styled as a full-width band rather than a grid cell. */
.gallery-cta {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 7px;
  margin-top: clamp(12px, 2.6vw, 18px);
  padding: 22px 18px;
  border-radius: 8px;
  border: 1px dashed rgba(124, 143, 116, 0.55);
  background:
    radial-gradient(110px 110px at -12% -15%, rgba(242, 216, 207, 0.55), transparent 72%),
    rgba(169, 186, 155, 0.1);
  text-align: center;
}
.gallery-cta__hash {
  margin: 0;
  font-family: var(--font-script);
  font-size: clamp(21px, 3.2vw, 25px);
  line-height: 1.15;
  color: var(--rose-deep);
  word-break: break-word;
}
.gallery-cta__text {
  margin: 0;
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.45;
}

@media (min-width: 640px) {
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ── Official flyer / banner (optional, hideable) ───────────────────────
   A wide framed slot for couples who already have a designed flyer or
   poster for the event — guests can confirm they're in the right place.
   Clicking it opens the same full-screen viewer as the photos (with the
   same zoom). Same double-frame mat language as the gallery pictures. */
.banner-flyer {
  position: relative;
  margin: clamp(28px, 5.5vmin, 44px) auto 0;
  max-width: 660px;
  text-align: center;
}
.banner-flyer__link { display: block; text-decoration: none; }
.banner-flyer__link:focus-visible { outline: 2px solid var(--rose); outline-offset: 2px; }
.banner-flyer img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 8 / 3;
  object-fit: cover;
  padding: 6px;
  background: var(--card);
  border: 1px solid rgba(169, 106, 96, 0.3);
  border-radius: 4px;
  box-shadow: 0 14px 30px -22px rgba(120, 92, 78, 0.5), inset 0 0 0 3px rgba(124, 143, 116, 0.12);
}
.banner-flyer figcaption {
  margin-top: 8px;
  font-family: var(--font-script);
  font-size: clamp(19px, 3vw, 22px);
  color: var(--rose-ink);
}

/* ── Optional film feature (video URL embed) ────────────────────────────
   A poster + play button that lazy-loads an <iframe> from the URL in
   data-video-url (YouTube / Vimeo / any embeddable URL). Nothing loads
   until the guest taps play — fast, and consent-friendly; the iframe can
   go fullscreen for a proper cinema view. The play medallion echoes the
   card's arched cover portrait. Delete the block to hide the feature. */
.film-card { margin-top: clamp(28px, 5.5vmin, 44px); text-align: center; }
.film-frame {
  position: relative;
  width: 100%;
  max-width: 640px;
  margin: clamp(16px, 3.4vmin, 24px) auto 0;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 10px;
  background: var(--paper-deep); /* mat behind the poster / while loading */
  border: 1px solid rgba(169, 106, 96, 0.3);
  box-shadow: 0 16px 34px -24px rgba(120, 92, 78, 0.5), inset 0 0 0 3px rgba(124, 143, 116, 0.12);
}
.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;
}
.film-play {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(56px, 12vmin, 72px); height: clamp(56px, 12vmin, 72px);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--rose-ink);
  background: radial-gradient(circle at 32% 28%, #FFF6F1, var(--card) 60%, var(--paper-deep));
  border: 1px solid rgba(201, 138, 127, 0.5);
  box-shadow: 0 12px 28px -12px rgba(120, 92, 78, 0.65);
  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-open:focus-visible { outline: 2px solid var(--rose); outline-offset: 3px; }
.film-hint {
  position: absolute; left: 50%; bottom: 14px;
  transform: translateX(-50%);
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(91, 78, 73, 0.62);
  color: #FBF7F0;
  font: 500 10px var(--font-ui);
  letter-spacing: 0.18em; text-transform: uppercase;
  pointer-events: none;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.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 18px;
  color: var(--muted);
  font: italic 500 14px var(--font-display);
  text-align: center;
  background: var(--paper-deep);
}
.film-external {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 14px;
  font: 500 11px var(--font-ui);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--rose-ink);
  text-decoration: none;
  border-bottom: 1px solid rgba(201, 138, 127, 0.4);
  padding-bottom: 2px;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.film-external:hover { color: var(--sage-deep); border-color: var(--sage-deep); }
.film-external[hidden] { display: none; }
@keyframes film-pulse {
  0%, 100% { box-shadow: 0 12px 28px -12px rgba(120, 92, 78, 0.65), 0 0 0 0 rgba(201, 138, 127, 0.35); }
  50% { box-shadow: 0 12px 28px -12px rgba(120, 92, 78, 0.65), 0 0 0 14px rgba(201, 138, 127, 0); }
}

/* ── Photo viewer (lightbox) — click any photo to see it full screen ─────
   Plain enhancement over the optional photo slots (#card-photo + the
   gallery). The viewer markup is built by main.js only when a photo slot
   exists, so removing those blocks removes the viewer entirely — nothing
   orphaned, no platform contract. */
html.js .card__cover a, html.js .gallery-img, html.js .banner-flyer__link, html.js .free-media img { cursor: zoom-in; }
.card__cover a, .banner-flyer__link { display: block; text-decoration: none; }
.card__cover a:focus-visible, .banner-flyer__link:focus-visible { outline: 2px solid var(--rose); outline-offset: 2px; }

/* Small "view full size" badge on hover / keyboard focus — shows every
   photo slot is clickable (the viewer zooms). Purely visual; without JS
   (no viewer) it never appears. Excludes the keepsake .gallery-cta card,
   which is not a photo. */
html.js .card__cover, html.js .gallery-item, html.js .banner-flyer { position: relative; }
html.js .card__cover::after,
html.js .gallery-item:not(.gallery-cta)::after,
html.js .banner-flyer::after {
  content: "";
  position: absolute;
  top: 10px; right: 10px;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: rgba(255, 253, 248, 0.94)
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23A96A60' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cpath d='M15.5 15.5L21 21'/%3E%3C/svg%3E")
    center / 16px no-repeat;
  border: 1px solid rgba(201, 138, 127, 0.4);
  box-shadow: 0 4px 12px -4px rgba(120, 92, 78, 0.45);
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  pointer-events: none;
}
html.js .card__cover:hover::after,
html.js .card__cover:focus-within::after,
html.js .gallery-item:hover::after,
html.js .gallery-item:focus-within::after,
html.js .banner-flyer:hover::after,
html.js .banner-flyer:focus-within::after {
  opacity: 1;
  transform: none;
}

.photo-viewer {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  background: rgba(62, 46, 41, 0.92);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  overflow: hidden; /* a zoomed/panned photo never paints past the backdrop */
}
.photo-viewer.is-open { display: flex; }
.photo-viewer figure {
  margin: 0;
  max-width: min(92vw, 960px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
}
.photo-viewer img {
  display: block;
  max-width: 100%;
  max-height: 76vh;
  max-height: 76dvh;
  width: auto;
  height: auto;
  border: 6px solid var(--card);
  border-radius: 4px;
  background: var(--paper-deep); /* mat while loading / if the file is missing */
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.75);
}
/* zoom + pan affordances (driven by main.js): click zooms to where you
   tap, drag pans when zoomed, pinch scales on touch, + / − / 0 keys on
   desktop. transform-origin 0 0 keeps the JS transform math simple. */
.photo-viewer img {
  transform-origin: 0 0;
  transition: transform 0.25s ease;
  touch-action: none;
  cursor: zoom-in;
}
.photo-viewer img.is-zoomed { cursor: grab; }
.photo-viewer img.is-grabbing { cursor: grabbing; }

/* "open original" — jumps straight to the real photo file in a new tab. */
.pv-original {
  position: absolute;
  bottom: calc(14px + env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid rgba(247, 234, 223, 0.4);
  background: rgba(255, 253, 248, 0.94);
  color: var(--rose-ink);
  font: 500 12px var(--font-ui);
  text-decoration: none;
  box-shadow: 0 10px 24px -12px rgba(0, 0, 0, 0.6);
  transition: background-color 0.2s ease;
}
.pv-original:hover { background: #fff; }
.pv-original[hidden] { display: none; }
.photo-viewer figcaption {
  font-family: var(--font-script);
  font-size: 24px;
  color: #F7EADF;
}
.photo-viewer .pv-unavailable {
  display: flex;
  align-items: center;
  justify-content: center;
  width: min(72vw, 520px);
  height: min(46vh, 360px);
  border: 6px solid var(--card);
  border-radius: 4px;
  background: var(--paper-deep);
  color: var(--muted);
  font-size: 14px;
  font-style: italic;
  text-align: center;
  padding: 0 18px;
}

.pv-btn {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(247, 234, 223, 0.5);
  background: rgba(255, 253, 248, 0.94);
  color: var(--rose-ink);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 10px 24px -12px rgba(0, 0, 0, 0.6);
  transition: transform 0.2s ease, background-color 0.2s ease;
}
.pv-btn:hover { transform: scale(1.06); background: #fff; }
.pv-close { top: calc(14px + env(safe-area-inset-top)); right: calc(14px + env(safe-area-inset-right)); }
.pv-prev  { left: 12px; top: 50%; transform: translateY(-50%); }
.pv-next  { right: 12px; top: 50%; transform: translateY(-50%); }
.pv-prev:hover, .pv-next:hover { transform: translateY(-50%) scale(1.06); }
.photo-viewer :focus-visible { outline: 2px solid var(--rose); outline-offset: 3px; }

/* Narrow screens: prev/next drop below the photo so they never cover it. */
@media (max-width: 520px) {
  .photo-viewer { flex-direction: column; gap: 10px; }
  .photo-viewer img { max-height: 62vh; max-height: 62dvh; }
  .pv-prev, .pv-next { position: static; transform: none; }
  .pv-prev:hover, .pv-next:hover { transform: scale(1.06); }
  .pv-prev, .pv-next { width: 40px; height: 40px; }
  .photo-viewer figure { flex: 1 1 auto; justify-content: center; }
  .photo-viewer .pv-close { top: calc(10px + env(safe-area-inset-top)); right: calc(10px + env(safe-area-inset-right)); }
  .pv-original { position: static; transform: none; margin: 0 0 2px; }
}

/* ── buttons ───────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 999px;
  border: 1px solid transparent;
  font: 500 14px var(--font-ui);
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.15s, box-shadow 0.2s, background 0.2s, border-color 0.2s, color 0.2s;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--rose); outline-offset: 2px; }
.btn--primary {
  background: linear-gradient(135deg, var(--rose) 0%, var(--rose-deep) 100%);
  color: #fff;
  box-shadow: 0 12px 28px rgba(201, 138, 127, 0.42);
}
.btn--primary:hover { filter: brightness(1.04); }
.btn--ghost { background: transparent; border-color: var(--line); color: var(--ink); }
.btn--ghost:hover { border-color: var(--rose); color: var(--rose-deep); }
.btn--block { width: 100%; }
.btn:disabled { opacity: 0.6; cursor: wait; }

/* loading node (driven by the platform script) */
.loading { display: none; text-align: center; color: var(--muted); font-size: 13px; padding: 10px 0 0; }

/* ── RSVP deadline countdown (platform-driven .count-*) ────────────────── */
.rsvp-countdown {
  margin: -6px 0 22px;
  padding: 12px 18px;
  border-radius: 12px;
  border: 1px dashed rgba(201, 138, 127, 0.6);
  background: rgba(242, 216, 207, 0.35);
  text-align: center;
  font-size: 13.5px;
}
.rsvp-countdown__label { margin: 0 0 4px; font: 500 10.5px var(--font-ui); letter-spacing: 0.26em; text-transform: uppercase; color: var(--rose-deep); }
.rsvp-countdown__nums { margin: 0; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--muted); }
.rsvp-countdown__nums span { color: var(--rose-ink); font-size: 17px; }

/* ── forms (RSVP + injected guestbook) ─────────────────────────────────── */
.rsvp-form { display: grid; gap: 16px; }

.form-group { display: grid; gap: 6px; }
.form-group label {
  font: 500 12px var(--font-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sage-deep);
}
.form-group label small {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  font: 400 15.5px var(--font-display);
  transition: border-color 0.2s, box-shadow 0.2s;
}
.form-group textarea { resize: vertical; min-height: 76px; }
.form-group input::placeholder,
.form-group textarea::placeholder { color: #B7A79F; }
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--rose);
  box-shadow: 0 0 0 3px rgba(217, 150, 139, 0.22);
}

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

.form-hint { margin: -8px 0 0; font-size: 13.5px; font-style: italic; color: var(--muted); }

.error { min-height: 16px; font: 500 12.5px var(--font-ui); color: #B4544A; }
.error:empty { display: none; }

.rsvp-extras {
  display: grid;
  gap: 16px;
  padding: 16px 16px 2px;
  border-radius: 14px;
  border: 1px dashed rgba(124, 143, 116, 0.5);
  background: rgba(169, 186, 155, 0.1);
  transition: opacity 0.3s, filter 0.3s;
}

/* decline state: dim party-size + meal fields (visual only — fields stay
   enabled and submission is never intercepted; the platform owns the form) */
form.is-declining .rsvp-extras { opacity: 0.45; filter: grayscale(0.5); }

.declined-note {
  display: none;
  margin: -4px 0 0;
  padding: 10px 14px;
  border-radius: 12px;
  background: var(--blush);
  color: var(--rose-ink);
  font: italic 500 15px var(--font-display);
  text-align: center;
}
form.is-declining .declined-note { display: block; }

/* ── guestbook (Style A: platform injects form + list into .guestbook-body) ── */
.guestbook-section #guestbookForm .form-group { margin-bottom: 14px; }
.guestbook-section #guestbookForm button[type="submit"] {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px auto 0;
  padding: 12px 26px;
  border-radius: 999px;
  border: none;
  background: linear-gradient(135deg, var(--rose) 0%, var(--rose-deep) 100%) !important;
  color: #fff !important;
  font: 500 14px var(--font-ui);
  cursor: pointer;
  box-shadow: 0 12px 28px rgba(201, 138, 127, 0.4);
}
.guestbook-section #guestbookForm button[type="submit"]:hover { filter: brightness(1.04); }

/* injected message list — neutralise the engine's inline scroll box */
.guestbook-section #comments-container {
  margin-top: 28px !important;
  border-top: 1px solid var(--line) !important;
  max-height: none !important;
  overflow: visible !important;
}
.guestbook-section .guestbook-entry {
  position: relative;
  padding: 15px 17px;
  margin-top: 12px;
  border-radius: 7px;
  border: 1px solid rgba(169, 106, 96, 0.22);
  /* a pressed-note feel: blush watercolor corner on flat ivory, framed by
     an inner sage mat — same language as the venue cards + invitation. */
  background:
    radial-gradient(95px 95px at -8% -12%, rgba(242, 216, 207, 0.5), transparent 72%),
    var(--card);
  box-shadow: 0 4px 16px rgba(120, 92, 78, 0.08), inset 0 0 0 3px rgba(124, 143, 116, 0.1);
  word-break: break-word;
}
.guestbook-section .guestbook-entry h5 { margin: 0 0 4px; font: 600 15px var(--font-display); color: var(--rose-ink); }
.guestbook-section .guestbook-entry p { margin: 0 0 4px; font-size: 15px; line-height: 1.55; color: var(--ink); }
.guestbook-section .guestbook-entry small { color: var(--muted); font-size: 12px; }

.guestbook-section .more-guestbook-wrap { text-align: center; margin-top: 18px; }
.guestbook-section #load-more {
  padding: 10px 24px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font: 500 14px var(--font-ui);
  cursor: pointer;
}
.guestbook-section #load-more:hover { border-color: var(--rose); color: var(--rose-deep); }

/* thank-you / closed states injected by the platform scripts */
.thank-you-message { text-align: center; padding: 26px 12px; }
.thank-you-message h3 { margin: 0 0 8px; font-family: var(--font-script); font-weight: 400; font-size: 34px; color: var(--rose-deep); }
.thank-you-message p { margin: 4px 0; color: var(--muted); font-size: 15px; line-height: 1.6; }

/* ── foot ─────────────────────────────────────────────────────────────── */
.foot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px clamp(14px, 3vw, 28px);
  padding-bottom: calc(8px + env(safe-area-inset-bottom));
  background: rgba(251, 247, 240, 0.9);
  color: var(--muted);
  border-top: 1px solid rgba(91, 78, 73, 0.08);
  font-size: 12.5px;
  white-space: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.foot::-webkit-scrollbar { display: none; }
.foot__dot { opacity: 0.5; }

/* ── Free Page (optional .panel-free — repeatable free blocks) ──────────
   One repeatable block per entry: title + free text + OPTIONAL photo. The
   panel ships as a normal panel; the render engine removes the whole
   .panel-free section when no blocks exist (or when the host deletes
   them), so the tab main.js builds from this panel disappears with it. */
.free-blocks {
  display: grid;
  gap: clamp(30px, 6vmin, 46px);
}
.free-block { margin: 0; text-align: center; }
.free-title {
  margin: 0 0 clamp(14px, 3vmin, 20px);
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(30px, 6vmin, 40px);
  line-height: 1.15;
  color: var(--rose-ink);
  overflow-wrap: anywhere;
}
.free-title:focus { outline: none; }
.free-media {
  margin: 0 auto clamp(16px, 3.2vmin, 22px);
  max-width: 560px;
}
/* A block whose photo was left empty renders without its <img> — collapse
   the wrapper so no empty frame shows (the engine removes the img, this
   hides the leftover figure). */
.free-media:empty { display: none; }
.free-media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  padding: 5px;
  background: var(--card);
  border: 1px solid rgba(169, 106, 96, 0.3);
  border-radius: 4px;
  /* inner sage mat = the double-frame motif of the invitation sheet */
  box-shadow: 0 12px 26px -18px rgba(120, 92, 78, 0.5), inset 0 0 0 3px rgba(124, 143, 116, 0.12);
}
.free-content {
  margin: 0 auto;
  max-width: 30em;
  text-align: left;
  font-size: clamp(15px, 3.4vmin, 17px);
  line-height: 1.75;
  color: var(--muted);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* ── Full-width powered-by brand strip ──────────────────────────────────
   A slim, full-width footer line between the panels and the foot bar in
   the JS 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 cockpit's Free Powered By field can
   override the whole line. */
html:not(.js) .powered-by {
  width: 100%;
  text-align: center;
  padding: 16px 16px 26px;
  border-top: 1px solid var(--line);
  font: 700 9.5px var(--font-ui);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}
html.js .powered-by {
  width: 100%;
  text-align: center;
  padding: 5px 14px;
  border-top: 1px solid rgba(91, 78, 73, 0.08);
  background: rgba(251, 247, 240, 0.9);
  font: 700 9px var(--font-ui);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}
.powered-by .link-footer { color: var(--rose-ink); }

/* ── Brand footer (placeholder) — .app-footer > .footer-brand ─────────────
   No-JS: a plain closing line at the end of the stacked document. JS shell:
   hidden where it starts (after </main>), then main.js (initFooter) tucks
   it into the last existing panel so it reads as the invitation's closing
   line — even when RSVP / Guestbook are stripped. The platform may inject
   its own brand here. */
html:not(.js) .app-footer { display: block; padding: 26px 18px 42px; text-align: center; }
html.js .app-footer { display: none; }
html.js .panel .app-footer {
  display: block;
  margin-top: clamp(30px, 6vmin, 48px);
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.footer-brand {
  margin: 0;
  font: 500 11px var(--font-ui);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}
.footer-brand-name {
  font-family: var(--font-script);
  font-size: 1.5em;
  text-transform: none;
  letter-spacing: 0.02em;
  color: var(--rose-ink);
}

/* ── focus visibility (global) ────────────────────────────────────────── */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--rose);
  outline-offset: 2px;
}

/* ── reveal-on-scroll (JS adds .is-visible; reduced-motion skips it) ──── */
[data-reveal] { opacity: 0; transform: translateY(14px); transition: opacity 0.55s ease, transform 0.55s ease; }
[data-reveal].is-visible { opacity: 1; transform: none; }

/* ── small screens: reclaim vertical space ────────────────────────────── */
@media (max-height: 640px) {
  .masthead { padding-top: 6px; padding-bottom: 6px; }
  .masthead__top { display: none; }
  .foot { display: none; }
}
@media (max-width: 520px) {
  .chip-btn { padding: 8px 10px; }
  .chip-btn__label { display: none; }
  .tab { padding: 11px 18px; } /* roomier touch targets on small screens */
  .form-row { grid-template-columns: 1fr; }
  .card-scene { padding-left: 10px; padding-right: 10px; }
  .card { padding-left: 22px; padding-right: 22px; }
}

/* ── reduced motion ───────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
html.no-motion *, html.no-motion *::before, html.no-motion *::after {
  animation: none !important;
  transition: none !important;
}
html.no-motion [data-reveal] { opacity: 1; transform: none; }

/* ── print: flatten to readable pages ─────────────────────────────────── */
@media print {
  html.js .app { height: auto; display: block; overflow: visible; }
  html.js .panel { display: block !important; overflow: visible !important; position: static; }
  html.js .panel.hero-panel.is-active { display: block !important; }
  .masthead__actions, .tabs, .foot, .no-print, .powered-by { display: none !important; }
  body { background: #fff; }
  .panel-inner { max-width: none; padding: 12px; }
  .card-scene { padding: 0; display: block; }
  .card {
    width: 100%;
    box-shadow: none;
    border: 1px solid rgba(169, 106, 96, 0.5);
    page-break-inside: avoid;
  }
  .countdown-tiles, .timeline-item, .event-item, .notes { page-break-inside: avoid; }
  .story-gallery, .banner-flyer, .film-card { page-break-inside: avoid; }
  .film-frame iframe { display: none; } /* a playing video can't print — the poster prints instead */
  .photo-viewer { display: none !important; }
  html.js .panel .app-footer { border-top: 1px solid var(--line); }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
