/* =========================================================
   Kiran & Magda — The Wedding
   ---------------------------------------------------------
   Identity: "Two houses, one thread."

   Dusty pink, beige and blush cream. Daylight, not evening.
   The page runs blush; the hero is rose; beige carries the
   panels and the rules.

   Cocoa is the only colour outside that brief, and it is
   there because the three warm tints are too close in value
   to set type against each other. It does the reading; the
   pinks do the feeling.

   The signature element is the WOVEN BAND: a plain-weave
   strip of rose and beige. It edges the hero,
   marks every seam in the page, and signs the RSVP button.

   To retune the whole site, edit the palette block below.
   ========================================================= */
:root {
  /* ----- Palette ----- */
  --rose:        #E4C4BB;  /* beige-rose — the hero ground      */
  --rose-soft:   #ECD6CD;  /* rose, lifted                      */
  --blush:       #FAF0EC;  /* blush cream — the page ground     */
  --beige:       #E6D9CB;  /* beige — panels                    */
  --beige-deep:  #CDBCA9;  /* beige — rules and hairlines       */

  /* Yellow enters as a thread, never as a ground. Honey is
     texture — the warp in the woven band, the light at the top
     of the hero — and is meant to sit low against the beige.
     Gold is the one that has to be seen: hairlines and small
     marks. Neither is ever used to set type. */
  --honey:       #E4BE6E;  /* the yellow thread                 */
  --gold:        #9C6E26;  /* gold hairlines and marks          */
  --ink:         #43312E;  /* body text                         */
  --ink-soft:    #5C4641;  /* secondary text (AA on all grounds)*/
  --cocoa:       #2A1C1A;  /* headings, marks, the dark thread  */

  /* ----- The hero photograph -----
     Sits behind the type as a faded wash, not as a picture.
     Swap the file and the whole hero follows. */
  --hero-photo:    url("./photos/hero.jpg");
  --hero-photo-sm: url("./photos/hero-sm.jpg");

  /* ----- Type ----- */
  --display: "Bodoni Moda", "Didot", "Times New Roman", serif;
  --script:  "Pinyon Script", "Snell Roundhand", "Apple Chancery", cursive;
  --label:   "Marcellus SC", "Optima", "Palatino", serif;
  --body:    "Manrope", -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;

  /* ----- Layout ----- */
  --maxw:  1120px;
  --readw: 620px;
  --pad-x: clamp(22px, 5.5vw, 64px);
  --gap-section: clamp(88px, 13vw, 168px);

  /* ----- Motion ----- */
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);

  /* ----- The woven band -----
     One plain-weave unit, 24 × 12, in beige, rose and honey.
     Any cocoa in here reads as perforation rather than cloth,
     so the band stays tonal and lets the hairline define its
     edges; the honey weft is what carries the yellow. */
  --weave: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='12'><rect width='24' height='12' fill='%23E6D9CB'/><rect x='0' y='0' width='12' height='12' fill='%23E4C4BB'/><rect x='0' y='4' width='12' height='4' fill='%23E4BE6E'/><rect x='12' y='4' width='12' height='4' fill='%23E4C4BB'/><rect x='12' y='0' width='12' height='2' fill='%23E4BE6E' opacity='0.55'/></svg>");

  /* Paper grain, reused on both grounds */
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='300'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.09  0 0 0 0 0.08  0 0 0 0 0.07  0 0 0 0.2 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* =========================================================
   Reset + base
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  background-image: var(--grain);
  background-size: 300px 300px;
  background-attachment: fixed;
  font-family: var(--body);
  font-size: 17px;
  font-weight: 350;
  line-height: 1.65;
  color: var(--ink);
  background: var(--blush);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
@media (min-width: 720px) { body { font-size: 18px; } }

img, svg, figure, picture { display: block; max-width: 100%; }
button { font: inherit; cursor: pointer; }
input, textarea, select, button { font: inherit; color: inherit; }
a { color: inherit; }

:where(a, button, input, textarea, select, summary):focus-visible {
  outline: 2px solid var(--cocoa);
  outline-offset: 3px;
  border-radius: 2px;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 50%; top: 0; z-index: 100;
  transform: translate(-50%, -120%);
  background: var(--blush); color: var(--ink);
  padding: 12px 20px; font-family: var(--label);
  letter-spacing: 0.12em; text-decoration: none;
}
.skip-link:focus-visible { transform: translate(-50%, 0); }

/* =========================================================
   Shared type roles
   ========================================================= */
.label {
  font-family: var(--label);
  font-size: clamp(10px, 1.5vw, 12px);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--cocoa);
  margin: 0;
}
.on-dark .label { color: var(--ink-soft); }

.section-title {
  text-wrap: balance;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-weight: 400;
  font-size: clamp(32px, 5.4vw, 54px);
  line-height: 1.08;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin: 18px 0 0;
}
.on-dark .section-title { color: var(--ink); }

.prose { max-width: var(--readw); margin: 0 auto; }
.prose p { margin: 0 0 1.15em; }
.prose p:last-child { margin-bottom: 0; }
.prose em { font-family: var(--display); font-style: italic; font-size: 1.1em; }

.on-dark { color: var(--ink-soft); }
.on-dark strong { color: var(--ink); font-weight: 500; }

/* =========================================================
   The woven band — the signature element
   ========================================================= */
.weave {
  height: 12px;
  background-image: var(--weave);
  background-repeat: repeat-x;
  background-size: auto 12px;
  transform-origin: center;
}

/* Section seam: a hairline, the weave, a hairline. */
.seam {
  display: block;
  width: min(100%, var(--maxw));
  margin: clamp(40px, 6vw, 72px) auto;
  padding: 0 var(--pad-x);
}
.seam__band {
  height: 12px;
  background-image: var(--weave);
  background-repeat: repeat-x;
  background-size: auto 12px;
  border-block: 1px solid rgba(67, 49, 46, 0.22);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 1.4s var(--ease-out-expo);
}
.seam.is-in .seam__band { transform: scaleX(1); }
.on-dark .seam__band { border-color: rgba(67, 49, 46, 0.22); }

/* =========================================================
   Section shell
   ========================================================= */
.section {
  padding: var(--gap-section) var(--pad-x);
}
.section__inner { max-width: var(--maxw); margin: 0 auto; }
.section--dark {
  background: var(--beige);
  position: relative;
  isolation: isolate;
}
.section--dark::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image: var(--grain);
  background-size: 300px 300px;
  opacity: 0.45;
  mix-blend-mode: multiply;
  pointer-events: none;
}

.section__head { text-align: center; margin-bottom: clamp(44px, 7vw, 76px); }

/* =========================================================
   Scroll reveal
   ========================================================= */
.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity 0.9s var(--ease-out-quart) var(--d, 0s),
    transform 0.9s var(--ease-out-quart) var(--d, 0s);
}
.reveal.is-in { opacity: 1; transform: none; }

/* =========================================================
   HERO — mobile first. A portrait canvas carrying the
   photograph as a rose-tinted wash, with the invitation set
   at the foot of it in the words and order of the printed
   save-the-date. On a phone the canvas is the screen; on a
   desktop it is centred and the blush runs out around it.
   ========================================================= */
.hero {
  min-height: 100svh;
  background: var(--blush);
  display: flex;
  justify-content: center;
}

.hero__frame {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  width: 100%;
  max-width: 540px;          /* portrait on any screen */
  min-height: 100svh;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: clamp(48px, 8vh, 80px) clamp(22px, 6vw, 40px) clamp(26px, 4vh, 40px);
  background:
    radial-gradient(ellipse at 50% 2%, rgba(228, 190, 110, 0.30) 0%, transparent 58%),
    radial-gradient(ellipse at 50% 8%, rgba(250, 240, 236, 0.34) 0%, transparent 68%),
    linear-gradient(176deg, #EACFC6 0%, #E4C4BB 70%, #E0BEB4 100%);
}

/* The cloth edges the canvas, top and bottom. */
.hero__frame::before,
.hero__frame::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 12px;
  z-index: 3;
  background-image: var(--weave);
  background-repeat: repeat-x;
  background-size: auto 12px;
}
.hero__frame::before { top: 0;    border-bottom: 1px solid rgba(67, 49, 46, 0.28); }
.hero__frame::after  { bottom: 0; border-top:    1px solid rgba(67, 49, 46, 0.28); }

/* Desktop: hairline selvedges so the canvas reads as a panel
   rather than as a column that ran out of room. */
@media (min-width: 620px) {
  .hero__frame {
    border-inline: 1px solid rgba(67, 49, 46, 0.18);
    box-shadow: 0 0 80px rgba(67, 49, 46, 0.10);
  }
}

/* The photograph, drained of its own colour so it reads as a
   tint of the rose ground rather than a competing image. */
.hero__photo {
  position: absolute; inset: 0; z-index: -3;
  background-image: var(--hero-photo);
  background-size: cover;
  background-position: 38% top;
  background-repeat: no-repeat;
  filter: grayscale(0.8) contrast(0.92) brightness(1.12);
  opacity: 0.34;
}
@media (max-width: 560px) {
  .hero__photo { background-image: var(--hero-photo-sm); }
}

/* Beige-rose breathed back over the lower half so the words stay
   crisp wherever the photograph happens to be busy. */
.hero__scrim {
  position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background:
    linear-gradient(to bottom,
      rgba(228, 196, 187, 0.55) 0%,
      rgba(228, 196, 187, 0.05) 20%,
      rgba(228, 196, 187, 0.30) 46%,
      rgba(228, 196, 187, 0.86) 68%,
      rgba(228, 196, 187, 0.97) 84%,
      rgba(228, 196, 187, 1) 100%);
}

.hero__grain {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0.4; mix-blend-mode: multiply;
  background-image: var(--grain);
  background-size: 300px 300px;
}

.hero__inner {
  align-self: end;
  text-align: center;
  position: relative;
  z-index: 1;
}

/* The invitation, set the way the printed card sets it: two
   ranks of tracked capitals, then "of the" and the family
   name dropped in as script. The script line is the display
   piece, so it is the one that scales with the viewport. */
.hero__invitation {
  margin: 0;
  font-weight: 400;
  color: var(--cocoa);
}

.hero__invited,
.hero__occasion {
  display: block;
  font-family: var(--display);
  text-transform: uppercase;
}
.hero__invited {
  font-size: clamp(11px, 3vw, 13px);
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  line-height: 1.5;
}
.hero__occasion {
  margin-top: 0.34em;
  font-size: clamp(17px, 5vw, 25px);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
  line-height: 1.24;
}

.hero__ofthe,
.hero__family {
  display: block;
  font-family: var(--script);
  text-transform: none;
  letter-spacing: 0;
  text-indent: 0;
  overflow-wrap: break-word;
}
.hero__ofthe {
  margin-top: 0.5em;
  font-size: clamp(17px, 4.6vw, 23px);
  line-height: 1.2;
  color: var(--ink);
}
/* Pinyon carries long ascenders and descenders, so this line
   is capped well under the width it could take, and given the
   line-height to sit in. */
.hero__family {
  margin-top: 0.06em;
  font-size: clamp(38px, 12.5vw, 64px);
  line-height: 1.28;
  color: var(--cocoa);
}

/* Rule — heart — rule, as on the card. */
.hero__rule {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 3vw, 16px);
  margin: clamp(20px, 4.5vw, 30px) auto clamp(18px, 4vw, 24px);
  max-width: 300px;
}
.hero__rule > span:not(.hero__heart) {
  flex: 1;
  height: 1px;
  background: var(--cocoa);
  opacity: 0.42;
}
.hero__heart {
  flex: none;
  font-size: 10px;
  line-height: 1;
  color: var(--gold);
  opacity: 1;
}

.hero__date,
.hero__place {
  margin: 0;
  font-family: var(--label);
  font-size: clamp(11px, 3vw, 13px);
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
}
.hero__place {
  margin-top: clamp(8px, 2vw, 11px);
  color: var(--ink-soft);
}

.hero__foot {
  display: flex;
  justify-content: center;
  margin-top: clamp(26px, 5vh, 44px);
  position: relative;
  z-index: 1;
}
.hero__scroll {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  padding: 6px 16px;
  color: var(--ink-soft);
  font-family: var(--label);
  font-size: 10px;
  letter-spacing: 0.3em;
  padding-left: calc(16px + 0.3em);
  text-transform: uppercase;
  text-decoration: none;
}
.hero__scroll::after {
  content: "";
  width: 1px; height: 34px;
  background: linear-gradient(to bottom, var(--cocoa), transparent);
  animation: scroll-pulse 2.8s var(--ease-out-quart) infinite;
}
@keyframes scroll-pulse {
  0%, 100% { opacity: 0.3; transform: scaleY(0.6); transform-origin: top; }
  50%      { opacity: 0.9; transform: scaleY(1);   transform-origin: top; }
}

/* ----- Load choreography ----- */
.hero__invited,
.hero__occasion,
.hero__ofthe,
.hero__family,
.hero__date,
.hero__place,
.hero__foot { opacity: 0; }
.hero__rule > span:not(.hero__heart) { transform: scaleX(0); transform-origin: center; }
.hero__heart { opacity: 0; }

.is-ready .hero__invited   { animation: rise 1s   var(--ease-out-expo) 0.20s forwards; }
.is-ready .hero__occasion  { animation: rise 1s   var(--ease-out-expo) 0.38s forwards; }
.is-ready .hero__ofthe     { animation: rise 1s   var(--ease-out-expo) 0.58s forwards; }
.is-ready .hero__family    { animation: rise 1.2s var(--ease-out-expo) 0.74s forwards; }
.is-ready .hero__rule > span:not(.hero__heart) { animation: draw 1.4s var(--ease-out-expo) 1.15s forwards; }
.is-ready .hero__heart     { animation: bloom 0.9s var(--ease-out-expo) 1.45s forwards; }
.is-ready .hero__date      { animation: rise 1s   var(--ease-out-expo) 1.55s forwards; }
.is-ready .hero__place     { animation: rise 1s   var(--ease-out-expo) 1.70s forwards; }
.is-ready .hero__foot      { animation: rise 1s   var(--ease-out-expo) 1.90s forwards; }

@keyframes rise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}
@keyframes draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes bloom {
  from { opacity: 0; transform: scale(0.4); }
  to   { opacity: 1; transform: none; }
}

/* Very narrow screens: let the address wrap rather than clip. */
@media (max-width: 400px) {
  .hero__rule { max-width: 220px; }
  .hero__date, .hero__place { letter-spacing: 0.14em; text-indent: 0.14em; }
}

/* =========================================================
   INVITATION — the words
   ========================================================= */
.invitation { text-align: center; }
.invitation .prose {
  font-size: clamp(18px, 2.3vw, 21px);
  line-height: 1.75;
  color: var(--ink-soft);
}
.invitation__lede {
  font-family: var(--display);
  font-optical-sizing: auto;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(24px, 4vw, 36px);
  line-height: 1.32;
  color: var(--ink);
  text-wrap: balance;
  max-width: 22ch;
  margin: 0 auto clamp(32px, 5vw, 52px);
}
.invitation__signoff {
  margin-top: clamp(36px, 6vw, 60px);
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(20px, 3vw, 26px);
  color: var(--cocoa);
}

/* =========================================================
   DETAILS — ceremony / reception, split by a hairline
   ========================================================= */
.details__grid {
  display: grid;
  gap: clamp(56px, 9vw, 72px);
  grid-template-columns: 1fr;
}
/* One venue, one card — no need for the split column. */
.details__grid--single { max-width: 520px; margin-inline: auto; }

@media (min-width: 800px) {
  .details__grid:not(.details__grid--single) {
    grid-template-columns: 1fr 1px 1fr;
    gap: clamp(40px, 6vw, 72px);
    align-items: stretch;
  }
  .details__rule {
    background: linear-gradient(to bottom, transparent, rgba(67,49,46,0.3) 18%, rgba(67,49,46,0.3) 82%, transparent);
  }
}
.details__rule { display: none; }
@media (min-width: 800px) { .details__rule { display: block; } }

.card { text-align: center; }
.card__title {
  font-family: var(--display);
  font-optical-sizing: auto;
  font-weight: 400;
  font-size: clamp(26px, 3.6vw, 34px);
  margin: 14px 0 22px;
  color: var(--ink);
}
.card__time {
  font-family: var(--label);
  font-size: clamp(28px, 4.6vw, 40px);
  letter-spacing: 0.04em;
  color: var(--cocoa);
  margin: 0 0 6px;
}
.card__venue {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(19px, 2.6vw, 23px);
  color: var(--ink);
  margin: 0 0 8px;
}
.card__address {
  color: var(--ink-soft);
  font-size: 16px;
  margin: 0 0 20px;
  white-space: pre-line;
}
.card__note {
  color: var(--ink-soft);
  font-size: 16px;
  margin: 0;
}

.link-quiet {
  display: inline-block;
  font-family: var(--label);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--gold);
  transition: color 0.3s var(--ease-out-quart), border-color 0.3s var(--ease-out-quart);
}
.link-quiet:hover { color: var(--cocoa); border-color: var(--cocoa); }
.on-dark .link-quiet { color: var(--ink); border-color: var(--gold); }
.on-dark .link-quiet:hover { color: var(--cocoa); }

/* Save the date row */
.details__cta {
  margin-top: clamp(48px, 7vw, 80px);
  text-align: center;
}

/* =========================================================
   REEL — rolling images of the couple
   ========================================================= */
.reel { padding-inline: 0; overflow: hidden; }
.reel .section__head { padding-inline: var(--pad-x); }

.reel__track {
  display: flex;
  gap: clamp(12px, 2vw, 22px);
  width: max-content;
  will-change: transform;
}
.reel__row { overflow: hidden; padding-block: clamp(6px, 1.2vw, 11px); }
.reel__row + .reel__row { margin-top: clamp(12px, 2vw, 22px); }

.reel__row--a .reel__track { animation: roll-left  64s linear infinite; }
.reel__row--b .reel__track { animation: roll-right 78s linear infinite; }

/* The track holds the photo list twice; -50% lands on the seam. */
@keyframes roll-left  { from { transform: translate3d(0,0,0); }        to { transform: translate3d(-50%,0,0); } }
@keyframes roll-right { from { transform: translate3d(-50%,0,0); }     to { transform: translate3d(0,0,0); } }

/* The reel does not pause. It used to stop on :hover, which on a phone is
   a tap — and a tapped hover latches until you touch something else, so a
   guest who touched a photo was left with a still reel and no way to see
   that it had ever moved. :focus-within went with it: the tracks are
   aria-hidden and hold nothing focusable, so it could never fire.

   Anyone who does not want the movement is still served: under
   prefers-reduced-motion the animation is off entirely and the rows
   become scrollable by hand instead. */

.reel__item {
  position: relative;
  flex: 0 0 auto;
  width: clamp(180px, 30vw, 300px);
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background: var(--beige);
  border: 1px solid rgba(67, 49, 46, 0.16);
}
.reel__row--b .reel__item { aspect-ratio: 4 / 3; width: clamp(230px, 38vw, 390px); }

.reel__item img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(0.92) contrast(1.02);
}

/* Until real photos land, each frame reads as an intentional
   pressed-cloth panel rather than a broken image. */
.reel__item--empty {
  display: grid;
  place-items: center;
  background:
    var(--weave) repeat-x bottom / auto 6px,
    var(--grain) 0 0 / 300px 300px,
    linear-gradient(150deg, #F2E6D6, #DFCBBE);
}
.reel__item--empty span {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(20px, 3.4vw, 28px);
  letter-spacing: 0.02em;
  color: rgba(67, 49, 46, 0.34);
}

/* =========================================================
   PRACTICAL — dress code and the palette guide
   ========================================================= */
.stack > * + * { margin-top: var(--gap-section); }

.dress { text-align: center; }

.swatches {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: clamp(14px, 2.6vw, 26px);
  margin: clamp(32px, 5vw, 48px) 0 0;
  padding: 0;
  list-style: none;
}
.swatch { text-align: center; }
.swatch__chip {
  width: clamp(52px, 9vw, 74px);
  height: clamp(52px, 9vw, 74px);
  border-radius: 50%;
  border: 1px solid rgba(67, 49, 46, 0.18);
  margin: 0 auto 10px;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.10);
}
.swatch__name {
  font-family: var(--label);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The palette guide, reproduced as it was sent. It is a tall portrait
   sheet, so it is capped by height as well as width — left to fill the
   column it would be taller than the screen on a phone and the swatches
   at the top would scroll out of view before the examples arrived. */
.guide { text-align: center; }
.guide__figure {
  margin: clamp(30px, 5vw, 44px) auto 0;
  max-width: 620px;
}
.guide__img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 78svh;
  object-fit: contain;
  margin: 0 auto;
  background: var(--beige);
  border: 1px solid var(--beige-deep);
  box-shadow: 0 18px 50px rgba(67, 49, 46, 0.12);
}
.guide__caption {
  max-width: 46ch;
  margin: clamp(16px, 3vw, 22px) auto 0;
  font-family: var(--body);
  font-size: 14px;
  line-height: 1.65;
  color: var(--ink-soft);
}


/* =========================================================
   RSVP
   ========================================================= */
.rsvp__form { max-width: 560px; margin: 0 auto; }

.field { margin-bottom: 26px; }
.field > label,
.fieldset__legend {
  display: block;
  font-family: var(--label);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 10px;
}
.field__hint {
  display: block;
  font-family: var(--body);
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-soft);
  margin-top: 6px;
}

.input, .select, .textarea {
  width: 100%;
  background: transparent;
  color: var(--ink);
  border: 0;
  border-bottom: 1px solid rgba(67, 49, 46, 0.28);
  padding: 12px 2px;
  font-size: 17px;
  transition: border-color 0.3s var(--ease-out-quart);
}
.textarea { resize: vertical; min-height: 84px; line-height: 1.6; }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-bottom-color: var(--cocoa);
}
.input::placeholder, .textarea::placeholder { color: rgba(67, 49, 46, 0.38); }
.select { appearance: none; border-radius: 0; padding-right: 26px; cursor: pointer; }
.select option { background: var(--blush); color: var(--ink); }

.select-wrap { position: relative; }
.select-wrap::after {
  content: "";
  position: absolute; right: 4px; top: 50%;
  width: 8px; height: 8px;
  border-right: 1px solid var(--cocoa);
  border-bottom: 1px solid var(--gold);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

.fieldset { border: 0; padding: 0; margin: 0 0 26px; }

.choice-row { display: flex; gap: 12px; }
.choice { flex: 1; }
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice span {
  display: block;
  text-align: center;
  padding: 15px 10px;
  border: 1px solid rgba(67, 49, 46, 0.28);
  font-family: var(--label);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  transition: all 0.35s var(--ease-out-quart);
}
.choice input:checked + span {
  color: var(--blush);
  background: var(--cocoa);
  border-color: var(--cocoa);
}
.choice input:focus-visible + span { outline: 2px solid var(--cocoa); outline-offset: 3px; }

.conditional[hidden] { display: none; }

.btn {
  display: block;
  width: 100%;
  margin-top: 34px;
  padding: 18px 24px;
  background: var(--cocoa);
  color: var(--blush);
  border: 0;
  font-family: var(--label);
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  transition: background 0.35s var(--ease-out-quart), color 0.35s var(--ease-out-quart);
}
.btn:hover:not(:disabled) { background: var(--ink); }
.btn:disabled { opacity: 0.5; cursor: progress; }

/* The weave signs the button. */
.btn-wrap { position: relative; }
.btn-wrap::after {
  content: "";
  display: block;
  height: 8px;
  background-image: var(--weave);
  background-repeat: repeat-x;
  background-size: auto 8px;
}

.form-status {
  margin: 18px 0 0;
  min-height: 1.5em;
  font-size: 15px;
  text-align: center;
  color: var(--ink-soft);
}
.form-status.is-error { color: #94333B; }

.rsvp__thanks { text-align: center; }
.rsvp__thanks[hidden] { display: none; }

/* =========================================================
   FOOTER
   ========================================================= */
.foot {
  background: var(--beige);
  color: var(--ink-soft);
  text-align: center;
  padding: clamp(56px, 9vw, 88px) var(--pad-x);
}
.foot__mark {
  font-family: var(--display);
  font-size: clamp(30px, 5vw, 42px);
  color: var(--cocoa);
  margin: 0;
  letter-spacing: 0.06em;
}
.foot__mark i { font-style: italic; color: var(--rose); }
.foot__date {
  font-family: var(--label);
  font-size: 11px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  margin: 16px 0 0;
}

/* =========================================================
   Reduced motion — deliver the invitation, skip the theatre
   ========================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .hero__invited, .hero__occasion, .hero__ofthe, .hero__family,
  .hero__date, .hero__place { opacity: 1; animation: none; }
  .hero__foot { opacity: 1; animation: none; }
  .hero__rule > span:not(.hero__heart) { transform: none; animation: none; }
  .hero__heart { opacity: 1; transform: none; animation: none; }
  .hero__grain { animation: none; }
  .hero__scroll::after { animation: none; opacity: 0.6; }

  .reveal { opacity: 1; transform: none; transition: none; }
  .seam__band { transform: none; transition: none; }

  .reel__track { animation: none !important; }
  .reel__row { overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* The envelope is pure theatre, so it does not play at all —
     wedding.js also bails before it is ever shown. */
  .envelope { display: none !important; }

}

/* =========================================================
   GIFTS
   Rendered inside the thanks panel and nowhere else, so it is
   unreachable until a reply is in — the invitation never asks.
   The rows are a description list because that is what they are:
   a label and its value, not a table.
   ========================================================= */
.gift {
  max-width: 460px;
  margin: clamp(34px, 6vw, 56px) auto 0;
  padding: 0 clamp(20px, 4vw, 30px) clamp(22px, 4vw, 30px);
  background: var(--blush);
  border: 1px solid var(--beige-deep);
  text-align: center;
}
.gift[hidden] { display: none; }

/* The woven band signs the panel the way it signs the RSVP button. */
.gift__band {
  margin: 0 calc(-1 * clamp(20px, 4vw, 30px)) clamp(20px, 4vw, 28px);
  border-bottom: 1px solid var(--beige-deep);
}

.gift__title {
  margin: 8px 0 0;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-weight: 400;
  font-size: clamp(23px, 3.4vw, 30px);
  line-height: 1.15;
  color: var(--cocoa);
}
/* Scoped under .gift so these beat `.prose p`, which this panel sits
   inside and which would otherwise flatten their top margins to zero. */
.gift .gift__lede {
  margin: 14px 0 0;
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink);
  text-wrap: pretty;
}

/* The eyebrow needs the same scoping as the lede below: `.prose p`
   (0,1,1) outranks `.label` (0,1,0), so without this it picks up a
   1.15em bottom margin and drifts off the title. */
.gift .label { margin: 0; }

/* A ruled label over the account details. The rule is drawn either side
   of the words rather than across them, so it introduces the details
   without reading as a second panel. */
.gift .gift__or {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: clamp(22px, 4vw, 30px) 0 0;
  font-family: var(--label);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.gift__or::before,
.gift__or::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--beige-deep);
}

.gift__details {
  margin: clamp(14px, 2.5vw, 18px) 0 0;
  text-align: left;
}
.gift__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px 14px;
  padding: 12px 2px;
  border-bottom: 1px solid var(--beige-deep);
}
.gift__key {
  font-family: var(--label);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.gift__val {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
}
.gift__text {
  font-size: 17px;
  /* Tabular figures so a sort code and an account number line up
     rather than shifting about under proportional digits. */
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  color: var(--cocoa);
}
.gift__copy {
  flex: none;
  padding: 5px 10px;
  font-family: var(--label);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--beige-deep);
  cursor: pointer;
  transition: color 180ms, border-color 180ms, background 180ms;
}
.gift__copy:hover,
.gift__copy:focus-visible {
  color: var(--cocoa);
  border-color: var(--gold);
}
.gift__copy.is-done {
  color: var(--gold);
  border-color: var(--gold);
}

/* On a narrow screen the key, the value and the button cannot share a
   line — an account number is the one thing here that must never be
   clipped — so the row stacks and the value takes the full width. */
@media (max-width: 460px) {
  .gift__row {
    flex-direction: column;
    align-items: stretch;
    gap: 7px;
  }
  .gift__val {
    width: 100%;
    justify-content: space-between;
  }
}

.gift .gift__note {
  margin: clamp(18px, 3vw, 24px) 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* =========================================================
   THE GIFT DIALOG
   The panel is a native <dialog>, so the browser owns the top
   layer, the focus trap and Escape. What is styled here is the
   frame: the backdrop, the card, and the fact that a tall panel
   has to scroll inside the card rather than off the screen.
   ========================================================= */
.giftmodal {
  width: min(460px, calc(100vw - 32px));
  /* The panel is taller than a phone, so the card is capped and the
     panel scrolls inside it. The scroll sits on .gift rather than on
     the dialog, so the close button — absolutely placed against the
     card — stays pinned instead of scrolling away with the content. */
  max-height: min(86vh, 760px);
  padding: 0;
  border: 1px solid var(--beige-deep);
  background: var(--blush);
  color: var(--ink);
  box-shadow: 0 24px 70px rgba(67, 49, 46, 0.22);
  overflow: hidden;
}
/* display lives on [open], not on .giftmodal. The browser hides a closed
   dialog with `dialog:not([open]) { display: none }`, and that is a UA
   rule — any author display here outranks it and leaves the panel sitting
   in the page, account number and all, before a guest has replied. */
.giftmodal[open] {
  display: flex;
  flex-direction: column;
}

.giftmodal::backdrop {
  background: rgba(42, 28, 26, 0.55);
  backdrop-filter: blur(2px);
}

/* The card is the frame now, so the panel inside drops its own. */
.giftmodal .gift {
  max-width: none;
  margin: 0;
  border: 0;
  background: transparent;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* Above the details, below nothing. pointer-events stays off so the close
   button underneath it keeps taking clicks while the confetti falls. */
.giftmodal__confetti {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.giftmodal__close {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: color 180ms;
}
.giftmodal__close:hover,
.giftmodal__close:focus-visible { color: var(--cocoa); }

/* Held open by .is-giftopen on <html> while the dialog is up, so the
   page behind cannot scroll under it on the browsers that allow it. */
.is-giftopen,
.is-giftopen body { overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {
  .giftmodal[open] { animation: giftIn 320ms var(--ease-out-quart) both; }
  .giftmodal[open]::backdrop { animation: giftFade 320ms ease both; }
}
@keyframes giftIn {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes giftFade { from { opacity: 0; } to { opacity: 1; } }

/* =========================================================
   THE ENVELOPE
   ---------------------------------------------------------
   The one piece of theatre on the page. A sealed envelope in
   the site palette — beige body, rose flap, the woven band on
   its bottom edge, gold wax — which unseals, opens, and lifts
   the card out. As it fades, .is-ready releases the hero
   choreography underneath, so the two overlap rather than
   queue and the card appears to become the page.

   Everything here is decoration. The overlay is hidden in the
   markup and only shown by wedding.js, it is skipped entirely
   under reduced motion, and .is-locked hides it along with the
   rest of the page — a guest can never be shut out by it.
   ========================================================= */
.envelope {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  background: var(--blush);
  background-image: var(--grain);
  background-size: 300px 300px;
}
.envelope[hidden] { display: none; }

/* The whole overlay fades, not just the envelope inside it — otherwise
   the scene dissolves and leaves a blank ground, and the hero appears
   in a jump when the overlay is removed. Fading the veil lets the hero
   come up underneath while the card is still lifting. */
.envelope.is-opening {
  animation: env-veil 780ms var(--ease-out-quart) 1750ms forwards;
}

.envelope__scene {
  position: relative;
  width: clamp(258px, 70vw, 366px);
  aspect-ratio: 3 / 2;
  perspective: 1100px;
  transform-origin: 50% 58%;
  opacity: 0;
}
.envelope.is-opening .envelope__scene {
  animation:
    env-in  760ms var(--ease-out-expo) 60ms   forwards,
    env-out 760ms var(--ease-out-quart) 1750ms forwards;
}

/* ----- The envelope itself ----- */
.envelope__body {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--beige);
  border: 1px solid var(--beige-deep);
  box-shadow: 0 20px 44px -24px rgba(42, 28, 26, 0.5);
}

/* The front panel. It sits above the card in the stack and is tall
   enough to conceal it completely at rest — which is why its top edge
   is straight: a notch would let the card show through the two
   corners the flap's triangle doesn't cover. */
.envelope__pocket {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 88%;
  z-index: 3;
  background: var(--rose-soft);
  border: 1px solid var(--beige-deep);
  box-shadow: 0 -6px 12px -8px rgba(42, 28, 26, 0.45);
}

.envelope__weave {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 11px;
  z-index: 4;
  background-image: var(--weave);
  background-size: 22px 11px;
  border-top: 1px solid var(--gold);
}

.envelope__flap {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 54%;
  z-index: 5;
  background: var(--rose);
  border: 1px solid var(--beige-deep);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  transform-origin: 50% 0%;
  backface-visibility: visible;
}
.envelope.is-opening .envelope__flap {
  animation: env-flap 940ms var(--ease-out-quart) 640ms forwards;
}

/* The seal is a sibling, not a child of the flap: clip-path
   clips descendants, and inside the flap the triangle would
   cut the wax in half. */
.envelope__seal {
  position: absolute;
  top: 54%;
  left: 50%;
  z-index: 6;
  width: 58px;
  height: 58px;
  margin: -29px 0 0 -29px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #C99B4B, var(--gold) 64%);
  box-shadow:
    0 4px 12px -5px rgba(42, 28, 26, 0.62),
    inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}
.envelope__wax {
  font-size: 20px;
  line-height: 1;
  color: var(--blush);
  opacity: 0.92;
}
.envelope.is-opening .envelope__seal {
  animation: env-seal 560ms var(--ease-out-quart) 470ms forwards;
}

/* ----- The card ----- */
.envelope__card {
  position: absolute;
  bottom: 3%;
  left: 4%;
  z-index: 2;
  width: 92%;
  height: 82%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  /* The bottom of the card never leaves the envelope, so the extra
     padding there re-centres the type in the part you can actually see. */
  padding: 18px 18px 24%;
  background: var(--blush);
  border: 1px solid var(--beige-deep);
  box-shadow: 0 14px 30px -20px rgba(42, 28, 26, 0.5);
}
.envelope.is-opening .envelope__card {
  animation: env-card 1040ms var(--ease-out-expo) 1160ms forwards;
}

.envelope__mono {
  margin: 0;
  font-family: var(--script);
  font-size: clamp(38px, 11vw, 54px);
  line-height: 1;
  color: var(--cocoa);
}
.envelope__hair {
  width: 54px;
  height: 1px;
  background: var(--gold);
  opacity: 0.7;
}
.envelope__note {
  margin: 0;
  font-family: var(--label);
  font-size: 12px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.envelope__skip {
  position: absolute;
  right: clamp(16px, 4vw, 30px);
  bottom: clamp(16px, 4vw, 30px);
  padding: 9px 16px;
  font-family: var(--label);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--beige-deep);
  cursor: pointer;
  transition: color 200ms, border-color 200ms;
}
.envelope__skip:hover,
.envelope__skip:focus-visible { color: var(--cocoa); border-color: var(--gold); }

@keyframes env-in {
  from { opacity: 0; transform: translateY(16px) scale(0.965); }
  to   { opacity: 1; transform: none; }
}
@keyframes env-out {
  from { transform: none; }
  to   { transform: translateY(-26px) scale(1.06); }
}
@keyframes env-veil {
  from { opacity: 1; }
  to   { opacity: 0; }
}
@keyframes env-seal {
  0%   { transform: scale(1) rotate(0deg); opacity: 1; }
  38%  { transform: scale(1.14) rotate(-4deg); opacity: 1; }
  60%  { transform: scale(0.94) rotate(3deg); opacity: 1; }
  100% { transform: scale(0.8) rotate(16deg) translateY(-16px); opacity: 0; }
}
@keyframes env-flap {
  0%   { transform: rotateX(0deg); z-index: 5; }
  45%  { z-index: 0; }
  100% { transform: rotateX(-172deg); z-index: 0; }
}
@keyframes env-card {
  from { transform: translateY(0); }
  to   { transform: translateY(-80%); }
}

/* =========================================================
   THE GATE
   .is-locked is set by an inline script in <head> before the
   page paints, and removed by gate.js once a valid invite code
   is in hand. Everything but the unlock screen is hidden —
   including from screen readers, so a locked page does not
   read out the invitation.
   ========================================================= */
.is-locked body > *:not(.unlock) {
  display: none !important;
}
.is-locked,
.is-locked body {
  background: var(--blush);
}

.unlock {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: grid;
  place-items: center;
  padding: clamp(20px, 6vw, 48px);
  background: var(--blush);
  background-image: var(--grain);
  background-size: 300px 300px;
}
.unlock[hidden] { display: none; }

.unlock__card {
  width: 100%;
  max-width: 420px;
  text-align: center;
  background: #FFFCF9;
  border: 1px solid var(--beige-deep);
  box-shadow: 0 18px 60px rgba(67, 49, 46, 0.10);
}

/* The same cloth that edges the hero, top and bottom of the card. */
.unlock__band {
  height: 10px;
  background-image: var(--weave);
  background-repeat: repeat-x;
  background-size: auto 10px;
}

.unlock__eyebrow {
  margin: clamp(28px, 6vw, 38px) 0 10px;
  font-family: var(--label);
  font-size: 10px;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.unlock__title {
  margin: 0 0 14px;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(28px, 8vw, 38px);
  letter-spacing: 0.012em;
  text-transform: uppercase;
  color: var(--cocoa);
}
.unlock__lede {
  margin: 0 auto clamp(22px, 5vw, 30px);
  max-width: 30ch;
  padding: 0 22px;
  font-family: var(--body);
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--ink-soft);
}

.unlock__form {
  display: grid;
  gap: 10px;
  padding: 0 clamp(22px, 6vw, 34px);
}
.unlock__input {
  width: 100%;
  padding: 14px 16px;
  font-family: var(--label);
  font-size: 15px;
  letter-spacing: 0.22em;
  text-align: center;
  text-transform: uppercase;
  color: var(--cocoa);
  background: var(--blush);
  border: 1px solid var(--beige-deep);
  border-radius: 0;
}
.unlock__input::placeholder {
  letter-spacing: 0.14em;
  color: rgba(67, 49, 46, 0.38);
}
.unlock__input:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 1px;
}
.unlock__btn {
  padding: 14px 20px;
  font-family: var(--label);
  font-size: 11px;
  letter-spacing: 0.28em;
  text-indent: 0.28em;
  text-transform: uppercase;
  color: var(--blush);
  background: var(--cocoa);
  border: 0;
  cursor: pointer;
  transition: background 0.3s var(--ease-out-quart);
}
.unlock__btn:hover:not(:disabled) { background: var(--ink); }
.unlock__btn:disabled { opacity: 0.55; cursor: progress; }

.unlock__err {
  margin: 14px 22px clamp(26px, 6vw, 36px);
  min-height: 1.4em;
  font-family: var(--body);
  font-size: 13.5px;
  line-height: 1.5;
  color: #94333B;
}
