/* ==========================================================================
   heathandheather.wedding — Stage 7
   Every value below resolves to a token. No raw hex, no raw px for anything
   that has a token. (1px hairlines and 0 are the permitted exceptions.)
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  letter-spacing: var(--track-body);
  color: var(--page-fg, var(--fg-on-light));
  background: var(--ground-4);
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
h1, h2, p, dl, dd, figure { margin: 0; }

/* ---------------------------------------------------------------- veils -- */

.backdrop {
  position: fixed; inset: 0; z-index: 0;
  background-color: var(--page-ground, var(--ground-0));
}

/* Filmic grain. The storyboard's references are all photographic and grainy;
   a perfectly clean surface reads as vector, which reads as generated. */
.grain {
  position: fixed; inset: 0; z-index: var(--z-veil);
  pointer-events: none; opacity: 0.10; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/></filter><rect width='140' height='140' filter='url(%23n)' opacity='0.55'/></svg>");
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--c-ink); color: var(--c-gold);
  padding: var(--s-2) var(--s-3); font-family: var(--font-display);
  letter-spacing: var(--track-caps); text-transform: uppercase; font-size: var(--t-sm);
}
.skip:focus { left: var(--s-2); top: var(--s-2); }

main { position: relative; z-index: var(--z-page); }

/* ----------------------------------------------------------- typography -- */

.display {
  font-family: var(--font-display);
  font-weight: var(--w-regular);
  font-size: var(--t-2xl);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  max-width: var(--measure-tight);
  text-wrap: balance;
}

.eyebrow {
  font-family: var(--font-display);
  font-size: var(--t-sm);
  text-transform: uppercase;
  letter-spacing: var(--track-caps-wide);
  line-height: var(--lh-caps);
  color: var(--page-accent, var(--c-gold));
  text-indent: var(--track-caps-wide); /* optical: cancel trailing letterspace */
}
.eyebrow--quiet { color: var(--page-fg-muted, var(--fg-on-light-muted)); }

.names { margin: var(--s-3) 0 0; text-align: center; }
.name {
  display: block;
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: clamp(1.22rem, 0.95rem + 1.22vw, 2.35rem);
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
  line-height: var(--lh-tight);
  text-indent: var(--track-caps);
  white-space: nowrap;
}
.amp {
  display: block;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--t-md);
  letter-spacing: 0.02em;
  color: var(--page-accent, var(--c-gold));
  margin: var(--s-2) 0;
  text-transform: lowercase;
}

.detail {
  font-family: var(--font-display);
  font-size: var(--t-sm);
  text-transform: uppercase;
  letter-spacing: var(--track-caps);
  line-height: var(--lh-caps);
  text-indent: var(--track-caps);
}
.detail--venue { margin-top: var(--s-3); color: var(--page-fg-muted, var(--fg-on-light-muted)); }

.lede { font-size: var(--t-md); max-width: var(--measure); margin-top: var(--s-3); }
p { text-wrap: pretty; }
.quiet { color: var(--page-fg-muted, var(--fg-on-dark-muted)); font-style: italic; }
.fineprint {
  font-size: var(--t-xs); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--page-fg-muted, var(--fg-on-dark-muted)); margin-top: var(--s-2);
}

/* Invitation divider: hairline with a gilt lozenge. Taken from the paper. */
.orn {
  position: relative; width: min(14rem, 60%); height: var(--s-3);
  margin: var(--s-4) auto;
}
.orn::before {
  content: ""; position: absolute; inset-inline: 0; top: 50%;
  height: 1px; background: var(--page-accent, var(--c-gold)); opacity: 0.55;
}
.orn::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 0.42rem; height: 0.42rem; translate: -50% -50%; rotate: 45deg;
  background: var(--page-accent, var(--c-gold));
}

.monogram {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  font-family: var(--font-display); font-weight: var(--w-regular);
  font-size: var(--t-lg); letter-spacing: var(--track-caps);
  color: var(--page-accent, var(--c-gold)); text-indent: var(--track-caps);
}
.monogram i { width: 1px; height: 1.7em; background: currentColor; opacity: 0.55; }
.monogram--sm { font-size: var(--t-md); }

/* --------------------------------------------------------------- shared -- */

.s { position: relative; }

.link-gold {
  display: inline-block; margin-top: var(--s-4);
  font-family: var(--font-display); font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: var(--track-caps);
  text-indent: var(--track-caps);
  color: var(--page-accent, var(--c-gold)); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: var(--s-1);
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.link-gold:hover, .link-gold:focus-visible { color: var(--c-gold-lit); border-bottom-color: var(--c-gold-lit); }

.btn {
  display: inline-block; cursor: pointer;
  font-family: var(--font-display); font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: var(--track-caps); text-indent: var(--track-caps);
  padding: var(--s-2) var(--s-4); border-radius: var(--field-radius);
  text-decoration: none; transition: all var(--dur-fast) var(--ease-out);
}
.btn--ghost {
  color: var(--c-gold); border: var(--rule-gold); background: transparent;
  margin-top: var(--s-4);
}
.btn--ghost:hover, .btn--ghost:focus-visible {
  background: var(--c-gold); color: var(--c-ink); box-shadow: var(--glow-close);
}

:where(a, button, input, select, textarea):focus-visible {
  outline: 2px solid var(--c-gold-lit); outline-offset: 3px;
}

/* ------------------------------------------------------- 1 · INVITATION -- */

.s-invitation {
  min-height: 100svh;
  display: grid; place-items: start center;
  padding-top: clamp(var(--s-5), 8vh, var(--s-7));
  overflow: hidden;
}

/* The signature move. Round-topped aperture, cropped by the viewport at its
   base — which is the page's only invitation to keep going. */
/* The hero arch is the couple's own invitation border, redrawn: shell at the
   crown, acanthus at the corners, floral urns on the capitals, foliate shafts.
   Generated on black and keyed to alpha, so it lays over the parchment ground
   without a card or a box around it. */
.arch { position: relative; width: min(54rem, 92vw); }

/* Percentages are of the arch WIDTH, and the box has a fixed aspect ratio, so
   these land the type inside the opening at every size. */
.arch__inner {
  position: relative; z-index: 1;
  text-align: center;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s-2);
}
.arch__inner .eyebrow + .eyebrow { margin-top: calc(var(--s-1) * -1); }
.arch__inner .detail { margin-top: var(--s-2); }

/* Device 1 — type assembly. Lines letterpress in, in reading order. */
[class*=" t"], [class^="t"] { }
.t1,.t2,.t3,.t4,.t5,.t6,.t7,.t8 {
  opacity: 0; transform: translateY(0.5rem);
  animation: press var(--dur-med) var(--ease-out) forwards;
}
.t1 { animation-delay: 140ms }  .t2 { animation-delay: 225ms }
.t3 { animation-delay: 340ms }  .t4 { animation-delay: 430ms }
.t5 { animation-delay: 505ms }  .t6 { animation-delay: 630ms }
.t7 { animation-delay: 715ms }  .t8 { animation-delay: 800ms }
.monogram { opacity: 0; animation: press var(--dur-med) var(--ease-out) 60ms forwards; }
@keyframes press { to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------- 2 · THE DAY -- */

.s-day {
  min-height: 100svh; display: grid; place-items: center;
  padding: clamp(var(--s-4), 5vh, var(--s-6)) var(--s-3);
}
/* Device 2 — arch aperture. A round-topped mask scales open; the visitor
   passes through the arch for the first time. */
.aperture {
  width: min(54rem, 92vw);
  padding-block: clamp(var(--s-5), 7vh, var(--s-7));
  text-align: center;
}
.aperture__inner {
  display: flex; flex-direction: column; align-items: center;
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--dur-veil) var(--ease-soft);
}
.aperture.is-open .aperture__inner { clip-path: inset(0 0 0 0); }
.aperture .display { margin-inline: auto; }

.facts {
  margin-top: var(--s-2); display: grid; gap: var(--s-3);
  font-family: var(--font-display); font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: var(--track-caps); line-height: var(--lh-caps);
}
.facts dt { color: var(--page-accent, var(--c-gold)); text-indent: var(--track-caps); }
.facts dd { margin: 0; text-indent: var(--track-caps); color: var(--page-fg, inherit); }

/* -------------------------------------------------------- 3 · THE PLACE -- */

.s-place { height: 340vh; }
/* Device 3 — horizontal rail. The only sideways motion on the page. */
.rail {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: grid; grid-template-rows: auto 1fr; grid-template-columns: minmax(0, 1fr);
  align-content: center; gap: var(--s-5); padding: var(--s-6) 0;
}
.rail__copy {
  max-width: var(--measure); margin-inline: auto;
  padding-inline: var(--s-3); text-align: center;
  display: flex; flex-direction: column; gap: var(--s-3); align-items: center;
}
.rail__copy .display { margin-inline: auto; }
.rail__track {
  display: flex; width: max-content; gap: clamp(var(--s-3), 4vw, var(--s-6));
  padding-inline: 12vw; align-items: center;
  will-change: transform;
}
.plate { flex: 0 0 auto; width: min(52rem, 78vw); }
.plate img {
  width: 100%; height: clamp(16rem, 42vh, 28rem); object-fit: cover;
  border: 1px solid var(--c-gold-deep); box-shadow: var(--shadow-deep);
}
/* Shown whole, not cropped to the band. The plate width follows the picture so
   the strip keeps one height — cover was trimming the bottom off the sign. */
.plate--whole { width: auto; }
.plate--whole img { width: auto; height: clamp(16rem, 42vh, 28rem); object-fit: contain; }

/* ---------------------------------------------------------- 4 · STAYING -- */

.s-stay { display: grid; }
/* Device 4 — pinned still. Deliberately motionless after the rail. */
.stay__still { grid-area: 1 / 1; position: sticky; top: 0; height: 100svh; }
.stay__still img { width: 100%; height: 100%; object-fit: cover; opacity: 0.34; }
.stay__still::after {
  content: ""; position: absolute; inset: 0;
  /* Fade to the LIVE page ground, not a fixed token. The ground is still
     interpolating here, so a hard-coded oxblood left a visible seam where the
     image stopped and the next section began. */
  background: linear-gradient(180deg,
    var(--page-ground, var(--ground-2)) 0%, transparent 26%,
    transparent 68%, var(--page-ground, var(--ground-3)) 100%);
}
.stay__beats {
  grid-area: 1 / 1; position: relative; z-index: 2;
  max-width: var(--measure); margin-inline: auto;
  padding: 46vh var(--s-3);
  display: flex; flex-direction: column; gap: var(--s-5);
  text-align: center; align-items: center;
}
.stay__beats .display { margin-inline: auto; }

/* ------------------------------------------------------------ 5 · REPLY -- */

.s-reply { padding: var(--section-pad-y) var(--s-3); }
.reply__head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.reply__head .display { margin-inline: auto; }
.reply__head .eyebrow { margin-top: var(--s-2); }

/* -------------------------------------------------------- 6 · HONEYMOON -- */

.s-honeymoon {
  padding: var(--section-pad-y) var(--s-3);
  display: grid; gap: clamp(var(--s-5), 6vw, var(--s-7));
  grid-template-columns: 1fr; place-items: center;
  max-width: 68rem; margin-inline: auto;
}
@media (min-width: 56rem) { .s-honeymoon { grid-template-columns: 0.9fr 1.1fr; } }
/* Device 6 — framed plate, lit as if by one candle. */
.framed { position: relative; max-width: 26rem; }
.framed img { border: 1px solid var(--c-gold-deep); box-shadow: var(--glow-candle), var(--shadow-deep); }
.honeymoon__copy { max-width: var(--measure); display: flex; flex-direction: column; gap: var(--s-3); }

/* ------------------------------------------------------------ 7 · CLOSE -- */

.s-close {
  padding: var(--s-6) var(--s-3) var(--s-6);
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  text-align: center;
}
.close__names {
  font-family: var(--font-display); font-size: var(--t-lg);
  letter-spacing: var(--track-display); color: var(--fg-on-dark);
}
.close__meta {
  font-family: var(--font-display); font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: var(--track-caps-wide);
  text-indent: var(--track-caps-wide); color: var(--fg-on-dark-muted);
}

/* ----------------------------------------------------------- the anchor -- */
/* The only fixed chrome on the page. No nav bar. */
.anchor-rsvp {
  position: fixed; right: var(--s-3); bottom: var(--s-3); z-index: var(--z-form);
  font-family: var(--font-display); font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: var(--track-caps); text-indent: var(--track-caps);
  color: var(--c-gold); text-decoration: none;
  padding: var(--s-2) var(--s-3);
  border: var(--rule-gold); border-radius: var(--field-radius);
  background: rgba(10, 9, 2, 0.72); backdrop-filter: blur(6px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out), background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.anchor-rsvp.is-shown { opacity: 1; pointer-events: auto; }
.anchor-rsvp:hover, .anchor-rsvp:focus-visible { background: var(--c-gold); color: var(--c-ink); }

/* ------------------------------------------------------ reveal (shared) -- */

[data-reveal] {
  opacity: 0; transform: translateY(1.2rem);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
[data-reveal].is-in { opacity: 1; transform: none; }

/* -------------------------------------------------------------- mobile -- */

@media (max-width: 30rem) {
  :root { --track-caps-wide: 0.22em; --track-caps: 0.12em; }
  .plate img { height: 15rem; }
  .stay__beats { padding-block: 38vh; }
}

/* ----------------------------------------------------- reduced motion --- */

@media (prefers-reduced-motion: reduce) {
  .t1,.t2,.t3,.t4,.t5,.t6,.t7,.t8, .monogram { opacity: 1; transform: none; animation: none; }
  .aperture__inner { clip-path: none; }
  [data-reveal] { opacity: 1; transform: none; }
  .rail__track { transform: none !important; flex-wrap: wrap; justify-content: center; padding-inline: var(--s-3); }
  .s-place { height: auto; }
  .rail { position: static; height: auto; }
  .plate { width: min(40rem, 90vw); }
  .anchor-rsvp { opacity: 1; pointer-events: auto; }
}

/* ======================================================== 3 · THE PORTRAIT --
   Device: framed portrait. The empty gilt frame is the honest placeholder for
   a photograph that does not exist yet; the real engagement photo replaces the
   asset and the caption without touching the layout. */

.s-portrait {
  min-height: 100svh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--s-4);
  padding: 0 var(--s-3) clamp(var(--s-5), 7vh, var(--s-7));
}
/* Crowns the frame, and closes the dead screen that used to sit between the
   arch aperture and the portrait. */
.swag {
  width: min(58rem, 94vw); aspect-ratio: 1600 / 1067;
  margin-bottom: calc(var(--s-8) * -1);
  background: url("./assets/floral-swag.webp") no-repeat center top / contain;
  opacity: 0.85;
}
.portrait { max-width: min(30rem, 82vw); text-align: center; }
.portrait img {
  width: 100%;
  border: 1px solid var(--c-gold-deep);
  box-shadow: var(--glow-candle), var(--shadow-deep);
}
.portrait__cap {
  margin-top: var(--s-4);
  font-family: var(--font-display); font-style: italic;
  font-size: var(--t-md); line-height: var(--lh-display);
  color: var(--page-fg-muted, var(--fg-on-dark-muted));
  max-width: 30ch; margin-inline: auto; text-wrap: balance;
}

/* =========================================================== 6 · REPLY -----
   The dark floral field, borrowed directly from the couple's menu card — used
   in the one place on the site where a guest chooses a meal. */

.s-reply { position: relative; isolation: isolate; }
.reply__field {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: url("./assets/texture-floral-dark.webp");
  background-size: cover; background-position: center;
  opacity: 0.26;
  /* The blooms FRAME the form rather than lying under it: densest at the edges
     of the section, clearing through the central column where the fields and
     their labels live. Same instinct as the menu card, adjusted for the fact
     that a screen has no ink density to fall back on. */
  --floral-veil: radial-gradient(68% 46% at 50% 50%,
                   transparent 0%, rgba(0,0,0,0.28) 46%, rgba(0,0,0,0.72) 72%, #000 100%);
  /* Two masks, intersected: the radial clears the centre so the fields sit on
     open ground, and the linear dissolves the plate's own top and bottom edges.
     Without the second one the section boundary reads as a hard horizontal cut,
     which showed up as soon as the form got shorter. */
  --floral-ends: linear-gradient(to bottom, transparent 0, #000 14%, #000 80%, transparent 100%);
  -webkit-mask-image: var(--floral-veil), var(--floral-ends);
          mask-image: var(--floral-veil), var(--floral-ends);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

/* ============================================================ the form ----- */

.rsvp {
  max-width: 34rem; margin: var(--s-6) auto 0;
  display: flex; flex-direction: column; gap: var(--s-4);
}
.field { display: flex; flex-direction: column; gap: var(--s-1); }

.rsvp label, .label-as-legend, .guest__legend {
  font-family: var(--font-display); font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: var(--track-caps);
  text-indent: var(--track-caps); color: var(--c-gold);
}
.opt { text-transform: none; letter-spacing: 0; font-style: italic; opacity: 0.7; }

.rsvp input[type="text"], .rsvp input[type="email"], .rsvp select, .rsvp textarea {
  width: 100%; font-family: var(--font-body); font-size: var(--t-base);
  color: var(--fg-on-dark); background: var(--field-bg);
  border: 1px solid var(--field-border); border-radius: var(--field-radius);
  padding: 0 var(--s-2); transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
  -webkit-appearance: none; appearance: none;
}
.rsvp input[type="text"], .rsvp input[type="email"], .rsvp select { height: var(--field-h); }
.rsvp textarea { padding: var(--s-2); line-height: var(--lh-body); resize: vertical; }
.rsvp select {
  background-image: linear-gradient(45deg, transparent 50%, var(--c-gold) 50%),
                    linear-gradient(135deg, var(--c-gold) 50%, transparent 50%);
  background-position: right var(--s-3) center, right calc(var(--s-3) - 5px) center;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
.rsvp :is(input, select, textarea):hover { background: var(--field-bg-hov); }
.rsvp :is(input, select, textarea):focus {
  outline: none; border-color: var(--field-border-focus); background: var(--field-bg-hov);
  box-shadow: var(--glow-close);
}
.rsvp option { background: var(--c-ink); color: var(--fg-on-dark); }

.hint { font-size: var(--t-sm); color: var(--fg-on-dark-muted); font-style: italic; }
.hint--wide { max-width: none; text-align: center; }

.err { font-size: var(--t-sm); color: var(--c-error); display: none; }
.err.is-shown { display: block; }
.err--form { text-align: center; margin-top: var(--s-2); }

.field--choice { gap: var(--s-2); }
.choices { display: grid; gap: var(--s-2); }
@media (min-width: 30rem) { .choices { grid-template-columns: 1fr 1fr; } }

/* Attending is a pair of real buttons, not a label wrapping a radio. The whole
   surface is the tap target; on a phone they stack full-width so each is a
   comfortable thumb-sized press. */
.choice {
  display: flex; align-items: center; justify-content: center; text-align: center;
  cursor: pointer; width: 100%;
  border: 1px solid var(--field-border); border-radius: var(--field-radius);
  padding: var(--s-2) var(--s-3); min-height: var(--field-h);
  background: var(--field-bg);
  font-family: var(--font-body); font-size: var(--t-base); color: var(--fg-on-dark);
  letter-spacing: var(--track-display); text-indent: 0; text-transform: none;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.choice:hover { background: var(--field-bg-hov); }
/* The selected reply fills with gilt — accept glows solid, decline stays quieter. */
.choice[aria-pressed="true"] { border-color: var(--c-gold); background: var(--c-gold); color: var(--c-ink); box-shadow: var(--glow-close); }
.choice--no[aria-pressed="true"] { background: var(--field-bg-hov); color: var(--fg-on-dark); box-shadow: none; }

.guests { display: flex; flex-direction: column; gap: var(--s-4); }
.guest {
  border: 1px solid var(--field-border); border-radius: var(--field-radius);
  padding: var(--s-3); margin: 0;
  display: flex; flex-direction: column; gap: var(--s-3);
}
.guest__legend { padding-inline: var(--s-2); }
.field--check { flex-direction: row; align-items: center; gap: var(--s-2); }
.field--check label {
  /* Grow the label to fill the row so the whole strip is tappable, with enough
     vertical padding to clear a comfortable touch target. */
  flex: 1; display: flex; align-items: center; cursor: pointer;
  padding-block: var(--s-2); min-height: var(--field-h);
  text-transform: none; letter-spacing: 0; text-indent: 0;
  font-family: var(--font-body); font-size: var(--t-sm); color: var(--fg-on-dark-muted);
}
.field--check input { accent-color: var(--c-gold); width: 1.05rem; height: 1.05rem; }

.linkish {
  align-self: flex-start; background: none; border: 0; cursor: pointer;
  /* A comfortable tap target: the text is small but the press surface is not. */
  padding: var(--s-2) 0; min-height: var(--field-h); display: inline-flex; align-items: center;
  font-family: var(--font-body); font-size: var(--t-sm); font-style: italic;
  color: var(--fg-on-dark-muted); text-decoration: underline; text-underline-offset: 3px;
}
.linkish:hover { color: var(--c-cedar); }

.btn--sm { align-self: center; font-size: var(--t-xs); padding: var(--s-1) var(--s-3); }
.btn--solid {
  background: var(--c-gold); color: var(--c-ink); border: var(--rule-gold);
  align-self: center; min-width: 16rem; min-height: var(--field-h);
}
.btn--solid:hover:not(:disabled) { background: var(--c-gold-lit); box-shadow: var(--glow-close); }
.btn--solid:disabled { opacity: 0.55; cursor: progress; }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.confirm {
  max-width: 34rem; margin: var(--s-6) auto 0; text-align: center;
  display: flex; flex-direction: column; gap: var(--s-3); align-items: center;
}
.confirm .display { margin-inline: auto; }
.confirm__list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-1); }
.confirm__list li {
  font-family: var(--font-display); font-size: var(--t-md);
  display: flex; gap: var(--s-2); justify-content: center; align-items: baseline;
}
.confirm__list .meal {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--track-caps);
  color: var(--c-gold);
}

/* --- form control corrections ---------------------------------------------
   `.guests { display:flex }` and the UA's `[hidden]{display:none}` have equal
   specificity, so the later rule won and the party fields showed before the
   guest had said they were coming. */
.rsvp .guests[hidden] { display: none; }

/* Checkboxes paint a stark white box that has no business in candlelight.
   Drawn from the tokens instead. (Radios are gone — attending is a pair of
   toggle buttons, see .choice.) */
.rsvp input[type="checkbox"] {
  -webkit-appearance: none; appearance: none;
  width: 1.05rem; height: 1.05rem; flex: 0 0 auto; margin: 0; padding: 0;
  border: 1px solid var(--c-gold-deep); background: transparent;
  display: grid; place-content: center; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.rsvp input[type="checkbox"]::after {
  content: ""; width: 0.5rem; height: 0.5rem; background: var(--c-gold);
  transform: scale(0); transition: transform var(--dur-fast) var(--ease-out);
}
.rsvp input:checked { border-color: var(--c-gold); }
.rsvp input:checked::after { transform: scale(1); }
.rsvp input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--c-gold-lit); outline-offset: 2px;
}


/* --- light-on-dark compensation (taste floor) -------------------------------
   Type set on a dark ground with light-ground metrics reads thin and blurry.
   Compensated on all three axes — weight, tracking, leading — and applied
   statically to the sections that are always dark, so nothing reflows mid-scroll. */
.s-portrait, .s-place, .s-stay, .s-reply, .s-honeymoon, .s-close {
  font-weight: var(--w-medium);
  letter-spacing: 0.012em;
  line-height: 1.66;
}
.s-portrait .display, .s-place .display, .s-stay .display,
.s-reply .display, .s-honeymoon .display {
  font-weight: var(--w-regular);   /* the display face already carries enough mass */
  letter-spacing: var(--track-display);
  line-height: var(--lh-display);
}


/* --- cascading florals ------------------------------------------------------
   Painted on black, keyed to alpha, hung down both edges of the page. They
   are absent on the paper and arrive as the light goes, so the page starts as
   the printed invitation and becomes the decorated room by the time a guest
   reaches the reply form. Opacity is driven from the light level in app.js. */
.decor {
  position: fixed; top: 0; bottom: 0; z-index: 0;
  width: clamp(7rem, 15vw, 17rem);
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: cover;
  opacity: var(--decor, 0);
  transition: opacity var(--dur-veil) linear;
}
.decor--l {
  left: 0; background-image: url("./assets/floral-cascade-a.webp"); background-position: left top;
  -webkit-mask-image: linear-gradient(to right, #000 42%, transparent 96%);
          mask-image: linear-gradient(to right, #000 42%, transparent 96%);
}
.decor--r {
  right: 0; background-image: url("./assets/floral-cascade-b.webp"); background-position: right top;
  transform: scaleY(-1);
  -webkit-mask-image: linear-gradient(to left, #000 42%, transparent 96%);
          mask-image: linear-gradient(to left, #000 42%, transparent 96%);
}

/* Below 60rem the content column reaches the edges and the florals would crowd
   the type rather than frame it. */
@media (max-width: 60rem) { .decor { display: none; } }
@media (prefers-reduced-motion: reduce) { .decor { transition: none; } }

/* --- the closing ornament, taken from the base of the invitation ----------- */
.close__orn {
  width: min(30rem, 76vw); aspect-ratio: 1600 / 1067;
  margin-top: var(--s-3);
  background: url("./assets/orn-cartouche.webp") no-repeat center / contain;
  opacity: 0.9;
}


/* ============================================= THE PAPER (sections 1 + 2) ---
   The arch is cut into crown, repeating shaft and plinth, so it spans both
   sections at whatever height their content happens to be. The columns run
   unbroken between them — a second arch at the top of the day section read as
   a hard division rather than a continuation. */

.paper { position: relative; }

.paper__arch {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  width: min(54rem, 92vw); margin-inline: auto; left: 0; right: 0;
  display: flex; flex-direction: column;
}
.paper__crown {
  flex: 0 0 auto; aspect-ratio: 1520 / 1176; position: relative; z-index: 1;
  background: url("./assets/arch-crown.webp") no-repeat center top / 100% auto;
}
/* The crown runs down into the shaft's own ornament and its tail is ramped out
   to nothing, so the columns rise underneath it and the join dissolves rather
   than cutting straight across. Overlap measured by _gen/split_arch.py. */
.paper__body { flex: 1 1 auto; position: relative; margin-top: -15.47%; }
.paper__plinth {
  flex: 0 0 auto; aspect-ratio: 1520 / 197;
  background: url("./assets/arch-plinth.webp") no-repeat center bottom / 100% auto;
}
/* Geometry measured off the plate by _gen/split_arch.py, not guessed. */
.paper__arch { --shaft-w: 14.4%; --shaft-x: 5.2%; }
/* The crown's tail and the column top occupy the same band. Left stacked they
   both paint and the gilt doubles into a bright seam, so the shaft fades IN
   over exactly the distance the crown fades OUT — 15.47% of the arch width,
   the overlap reported by _gen/split_arch.py. */
.paper__shaft {
  position: absolute; top: -1px; bottom: -1px; width: var(--shaft-w);
  background-repeat: repeat-y; background-size: 100% auto;
  --fade: min(8.35rem, 14.23vw);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade));
          mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade));
}
.paper__shaft--l { left: var(--shaft-x);  background-image: url("./assets/arch-shaft-l.webp"); }
.paper__shaft--r { right: var(--shaft-x); background-image: url("./assets/arch-shaft-r.webp"); }

.paper .s { position: relative; z-index: 1; }
/* Clear the columns on both sides; 5.2% inset + 14.4% shaft = 19.6%. */
.s-invitation .arch__inner { padding: 33.5% 21% 4%; }
.s-day .aperture { padding-inline: 21%; }

@media (max-width: 52rem) {
  .paper__arch { width: 100%; }
  /* Narrower columns on a phone: at full width the opening was 228px and the
     names would not clear it. The ornament scales with the shaft, so the
     proportion holds. */
  .paper__arch { --shaft-w: 10%; --shaft-x: 2.5%; }
  .s-invitation .arch__inner { padding: 46% 15% 5%; }
  .s-day .aperture { padding-inline: 15%; }
}

/* --- finding your household -------------------------------------------------
   A type-ahead rather than a list of everyone invited: a guest sees only their
   own household, never anyone else's. */

.rsvp--find { gap: var(--s-3); }

.hh-results { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.hh-pick {
  width: 100%; text-align: left; cursor: pointer;
  display: flex; flex-direction: column; justify-content: center; gap: 0.15rem;
  padding: var(--s-2) var(--s-3); min-height: var(--field-h);
  background: var(--field-bg); color: var(--fg-on-dark);
  border: 1px solid var(--field-border); border-radius: var(--field-radius);
  font-family: var(--font-body); font-size: var(--t-base);
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.hh-pick:hover, .hh-pick:focus-visible { border-color: var(--c-gold); background: var(--field-bg-hov); }
.hh-name { font-family: var(--font-display); font-size: var(--t-md); }
.hh-where {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--track-caps);
  color: var(--fg-on-dark-muted);
}
.hh-none {
  padding: var(--s-2) var(--s-3); font-style: italic;
  color: var(--fg-on-dark-muted); font-size: var(--t-sm);
}

.hh-chosen {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: center; gap: var(--s-2);
  padding-bottom: var(--s-3); border-bottom: 1px solid var(--field-border);
}
.hh-chosen__name { font-family: var(--font-display); font-size: var(--t-lg); color: var(--fg-on-dark); }
/* .linkish sets align-self:flex-start for the "Remove" button inside a column,
   which overrode the container's baseline and floated this one up to the name's
   cap height. Put it back on the baseline, and drop the tap-target padding here
   so it doesn't inflate the name row. */
.hh-chosen .linkish { align-self: center; padding-block: 0; min-height: 0; }

.link-plain { color: var(--c-gold); text-underline-offset: 3px; }


/* --- the bar ----------------------------------------------------------------
   Three links, not the five-plus row the template formula uses. It stays out of
   the way over the invitation — the page should open as the paper, with nothing
   above it — and fades in once the visitor has passed the hero. */
.topbar {
  position: fixed; inset: 0 0 auto; z-index: var(--z-form);
  display: flex; justify-content: center; gap: clamp(var(--s-3), 4vw, var(--s-6));
  padding: var(--s-2) var(--s-3);
  background: rgba(10, 9, 2, 0.62);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(192, 169, 127, 0.22);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out);
}
.topbar.is-shown { opacity: 1; pointer-events: auto; }
.topbar a {
  font-family: var(--font-display); font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: var(--track-caps); text-indent: var(--track-caps);
  color: var(--c-gold); text-decoration: none; white-space: nowrap;
  padding: var(--s-1) 0; border-bottom: 1px solid transparent;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.topbar a:hover, .topbar a:focus-visible { color: var(--c-gold-lit); border-bottom-color: var(--c-gold-lit); }

@media (max-width: 30rem) {
  .topbar { gap: var(--s-3); padding-inline: var(--s-2); }
  .topbar a { font-size: 0.66rem; letter-spacing: 0.08em; text-indent: 0.08em; }
}
@media (prefers-reduced-motion: reduce) { .topbar { opacity: 1; pointer-events: auto; } }

/* --- the review --------------------------------------------------------------
   Nothing is saved until Confirm. The reply is shown back in the same register
   the invitation uses, because this is the last look before it becomes the
   caterer's count. */
.review {
  max-width: 34rem; margin: var(--s-6) auto 0; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
}
.review .display { margin-inline: auto; }
.review__verdict {
  font-family: var(--font-display); font-size: var(--t-sm);
  text-transform: uppercase; letter-spacing: var(--track-caps); text-indent: var(--track-caps);
  color: var(--c-gold);
}
.review .confirm__list {
  width: 100%; max-width: 26rem;
  border-block: 1px solid var(--field-border); padding-block: var(--s-3);
}
.review .confirm__list li { justify-content: space-between; text-align: left; gap: var(--s-3); }
.review__aside {
  font-size: var(--t-sm); color: var(--fg-on-dark-muted);
  max-width: 30rem; font-style: italic;
}
.cap-label {
  display: block; font-family: var(--font-display); font-style: normal;
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--track-caps);
  color: var(--c-gold); margin-bottom: var(--s-1);
}
.review__actions {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  margin-top: var(--s-2);
}
.review__actions .linkish { align-self: center; }
