/* ---------- Tokens (from Figma) ---------- */
:root {
  --wine: #6b303f;
  --ivory: #fcf9f3;
  --paper: #f7f1e7;
  --blush: #f5ede0;
  --ink: #432f32;
  --muted: #766768;
  --olive: #818571;
  --line: #cfbab0;
  --page: #e9e2d7;

  --font-serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --font-script: "Pinyon Script", "Snell Roundhand", cursive;
  --script-fix: 1;
  --font-sans: "Manrope", system-ui, -apple-system, sans-serif;

  /* Every screen in the mock is a 393px-wide phone column and everything below is placed in those
     pixels. On a narrower phone main.js sets --k below 1 and each stage is zoomed down to fit. */
  --k: 1;
  /* The screen's height, locked at load: in-app browsers (Telegram, Instagram) grow and shrink
     the viewport as their address bar collapses, and anything measured in svh would resize with
     it — backgrounds re-cropping, text shifting, the scroll jumping. */
  --vh: 100svh;
}

html[lang="ru"] {
  --font-script: "Marck Script", "Snell Roundhand", cursive;
  --script-fix: 0.88; /* Marck Script runs larger than Pinyon at the same size */
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

html { scroll-behavior: auto; } /* main.js glides instead */
html.is-locked { overflow: hidden; }

body {
  margin: 0;
  background: var(--page);
  color: var(--wine);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, p, ol, ul, fieldset, legend { margin: 0; padding: 0; font-weight: 400; }
ol, ul { list-style: none; }
fieldset { border: 0; min-width: 0; }
img { display: block; max-width: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

.app {
  position: relative;
  max-width: 393px;
  margin: 0 auto;
  overflow-x: clip;
  box-shadow: 0 0 60px rgba(67, 47, 50, 0.08);
}

/* Every screen fills the window and holds the mock's board in its middle, so no screen ends
   early with a strip of the next one showing under it. */
.screen {
  position: relative;
  min-height: calc(852px * var(--k)); /* the mock's own height: the same composition everywhere */
  display: grid;
  place-items: center;
  overflow: hidden;
  isolation: isolate;
}
.screen--pin { display: block; } /* the pinned screen sets its own height */
.screen--ivory { background: var(--ivory); }
.screen--paper { background: var(--paper); }
.screen--wine  { background: var(--wine); }

/* The fixed 393px column a screen is laid out in. */
.stage {
  position: relative;
  width: 393px;
  height: 852px;
  margin: 0 auto;
  zoom: var(--k);
}
/* The mock draws this screen 660px tall, shorter than the rest; the screen around it is full
   height like every other, so the board simply sits in the middle. */
.screen--date { min-height: calc(660px * var(--k)); }
.screen--date .stage { height: 660px; }
.screen--date .board {
  position: relative;
  width: 393px;
  height: 660px;
}

/* Absolutely placed block: --x / --y / --w come straight from the mock. */
.at {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
}
p.at, h2.at, h3.at { text-align: center; }

/* On a phone the page fills the screen edge to edge instead of the 393px column of the mock,
   and the hero takes the whole screen height. */
@media (max-width: 600px) {
  .app { max-width: none; box-shadow: none; }
}

/* Until the envelope is opened the page simply ends at it: the screens below are out of the
   document, so the phone stops there by itself instead of being pulled back by script. */
.is-held #date ~ .screen { display: none; }

/* Blocks lift into place the first time they are scrolled to. Nothing is hidden unless main.js
   has said it will bring it back. `translate` so it composes with any transform. */
.is-animated .rise { opacity: 0; translate: 0 16px; }
.is-animated .rise.is-in {
  opacity: 1;
  translate: none;
  transition: opacity 0.8s ease var(--in, 0s), translate 0.8s cubic-bezier(0.22, 0.61, 0.36, 1) var(--in, 0s);
}

/* ---------- Type ---------- */
.t-eyebrow { font: 400 10px/14px var(--font-sans); letter-spacing: -0.01em; }
.t-small   { font: 400 12px/16px var(--font-sans); letter-spacing: -0.01em; }
.t-body    { font: 400 14px/18px var(--font-sans); letter-spacing: -0.01em; }
.t-strong  { font-weight: 500; }

.t-label    { font: 500 18px/22px var(--font-serif); letter-spacing: -0.01em; }
.t-button   { font: 500 18px/22px var(--font-serif); letter-spacing: -0.01em; }
.t-month    { font: 500 22px/26px var(--font-serif); letter-spacing: -0.01em; }
.t-title-32 { font: 500 32px/36px var(--font-serif); letter-spacing: -0.01em; }
.t-title    { font: 500 38px/42px var(--font-serif); letter-spacing: -0.01em; }
.t-title-40 { font: 500 40px/44px var(--font-serif); letter-spacing: -0.01em; }
.t-date     { font: 500 38px/42px var(--font-serif); letter-spacing: -0.01em; }
.t-day      { font: 500 42px/46px var(--font-serif); letter-spacing: -0.01em; }
.t-name     { font: 500 54px/58px var(--font-serif); letter-spacing: -0.01em; }

.t-script-30 { font: 400 calc(30px * var(--script-fix)) / 34px var(--font-script); letter-spacing: -0.01em; }
.t-script-34 { font: 400 calc(34px * var(--script-fix)) / 38px var(--font-script); letter-spacing: -0.01em; }
.t-script-42 { font: 400 calc(42px * var(--script-fix)) / 46px var(--font-script); letter-spacing: -0.01em; }
.t-script-44 { font: 400 calc(44px * var(--script-fix)) / 48px var(--font-script); letter-spacing: -0.01em; }
.t-amp       { font: 400 calc(40px * var(--script-fix)) / 44px var(--font-script); letter-spacing: -0.01em; }

.t-wine  { color: var(--wine); }
.t-ink   { color: var(--ink); }
.t-muted { color: var(--muted); }
.t-olive { color: var(--olive); }

/* ---------- Shared pieces ---------- */
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 165px;
  height: 44px;
  border-radius: 6px;
  background: var(--wine);
  color: var(--ivory);
  font: 500 18px/22px var(--font-serif);
  letter-spacing: -0.01em;
  white-space: nowrap;
  transition: filter 0.2s ease, transform 0.2s ease;
}
.btn:active { transform: scale(0.97); }
@media (hover: hover) { .btn:hover { filter: brightness(1.12); } }
.btn--light { background: var(--paper); color: var(--wine); }

.heading { display: flex; flex-direction: column; gap: 8px; text-align: center; }
.heading--wide { gap: 18px; }

/* ---------- Preloader ---------- */
.loader {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 20px;
  background: var(--paper);
  transition: opacity 0.7s ease;
}
.loader__logo { width: 103px; height: 24px; opacity: 0.9; }
.loader__track {
  width: 132px;
  height: 1px;
  overflow: hidden;
  background: rgba(107, 48, 63, 0.16);
}
.loader__track i {
  display: block;
  width: calc(var(--load, 0) * 100%);
  height: 100%;
  background: var(--wine);
  transition: width 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.loader.is-done { opacity: 0; pointer-events: none; }

/* ---------- 0. Gate ---------- */
/* Fills the phone screen (the 393px column on a desktop). --u is one mock pixel at the "cover"
   scale of the 393×852 art, so the seal stays on the knot at any screen shape. */
.gate {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 50%;
  width: min(100%, calc(393px * var(--k)));
  translate: -50% 0;
  container-type: size;
  z-index: 50;
  overflow: hidden;
  cursor: pointer;
  outline: none;
}
.gate__leaf {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100%;
  overflow: hidden;
  transition: transform 1.5s cubic-bezier(0.65, 0, 0.3, 1);
}
.gate__leaf--left  { left: 0; }
.gate__leaf--right { right: 0; }
.gate__art {
  position: absolute;
  top: 0;
  width: 200%;
  height: 100%;
  object-fit: cover;
}
.gate__leaf--left .gate__art  { left: 0; }
.gate__leaf--right .gate__art { right: 0; }
.gate__art--untied { opacity: 0; } /* switched on the frame the clip ends, never faded */

/* The filmed untying, in the same framing as the two stills it was cut from. */
.gate__clip {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
}
.gate.is-playing .gate__clip { opacity: 1; }

.gate__seal {
  /* one pixel of the clip, at the scale it is displayed */
  --v: max(100cqw / 1080, 100cqh / 1920);
  position: absolute;
  left: 50%;
  top: calc(50% - 197 * var(--v)); /* the knot sits 71px above the clip's centre */
  width: calc(253 * var(--v));
  height: calc(253 * var(--v));
  translate: -50% 0;
}
.gate__half {
  position: absolute;
  inset: 0;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 2px 3px rgba(67, 47, 50, 0.18));
  transition: transform 0.18s cubic-bezier(0.2, 0.9, 0.3, 1), opacity 0.4s ease;
}
/* The wax gives: a shudder, a snap along the crack, then both halves drop away. */
.gate.is-cracking .gate__seal { animation: seal-shudder 0.26s ease-in-out; }
@keyframes seal-shudder {
  30% { transform: rotate(-2deg) scale(1.03); }
  70% { transform: rotate(2.5deg) scale(1.05); }
}
.gate.is-broken .gate__half { transition-duration: 0.1s; } /* the crack itself is a snap */
.gate.is-broken .gate__half--left  { transform: translate(-5%, 1%) rotate(-5deg); }
.gate.is-broken .gate__half--right { transform: translate(5%, 1.5%) rotate(6deg); }
.gate.is-falling .gate__half {
  opacity: 0;
  /* Thrown off fast and faded out well before they would reach the bottom of the frame. */
  transition: transform 0.52s cubic-bezier(0.3, 0, 0.7, 0.35), opacity 0.3s ease 0.16s;
}
.gate.is-falling .gate__half--left  { transform: translate(-58%, 210%) rotate(-62deg) scale(0.86); }
.gate.is-falling .gate__half--right { transform: translate(64%, 240%) rotate(74deg) scale(0.84); }

.gate.is-open .gate__art--untied { opacity: 1; }
.gate.is-parting .gate__leaf--left  { transform: translateX(-101%); }
.gate.is-parting .gate__leaf--right { transform: translateX(101%); }

/* ---------- 1. Invitation ---------- */
.screen--hero { background: var(--blush); }
.screen--hero .stage { height: 852px; }

.hero__garden {
  position: absolute;
  left: -19.5px;
  top: 0;
  bottom: 0;
  width: 433px;
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
  transform-origin: 50% 60%;
}
.is-animated .hero__garden { opacity: 0; transform: scale(1.04); }
.is-animated.is-revealed .hero__garden {
  opacity: 1;
  transform: none;
  transition: opacity 1.6s ease 0.3s, transform 2.4s cubic-bezier(0.22, 0.61, 0.36, 1) 0.3s;
}

/* ---------- Top bar ---------- */
.topbar {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 30;
  width: min(100%, calc(393px * var(--k)));
  height: calc(80px * var(--k));
  padding: calc(14px * var(--k)) calc(32px * var(--k)) 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  translate: -50% 0;
  opacity: 0;
  pointer-events: none;
  transition: translate 0.5s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.6s ease;
}
/* Nothing above the gate until it has been opened. */
.is-revealed .topbar { opacity: 1; pointer-events: auto; }
.topbar.is-hidden { translate: -50% -100%; }
/* A soft veil rather than a slab: it reaches past the bar and fades out to nothing, so there is
   no edge where it ends. No backdrop blur — its box cuts a hard line of its own on iOS. */
.topbar::before {
  content: "";
  position: absolute;
  inset: 0 0 -30% 0;
  z-index: -1;
  background: linear-gradient(180deg, var(--bar-tint, var(--paper)) 0%, var(--bar-tint, var(--paper)) 62%, transparent 100%);
  opacity: 0;
  transition: opacity 0.4s ease;
}
.topbar.is-solid::before { opacity: 1; }
.topbar.is-hidden::before { opacity: 0; transition: opacity 0.16s ease; } /* gone before the bar has left */
/* Over the wine screen the bar turns with it, rather than sitting there as a slab of cream. */
.topbar.is-dark .lang { color: var(--paper); }
.topbar.is-dark img { filter: brightness(0) invert(1); }
.topbar__logo { width: 103px; height: 24px; }
/* The bar's parts scale with the board so the masthead keeps the mock's proportions. Sized with
   calc rather than zoom: zoom inside a fixed bar loses taps in Chrome. */
.topbar__logo { width: calc(103px * var(--k)); height: calc(24px * var(--k)); }
.topbar__tools { display: flex; align-items: center; gap: calc(12px * var(--k)); }

.lang { position: relative; }
.lang__button {
  display: flex;
  align-items: center;
  gap: calc(4px * var(--k));
  font: 400 calc(14px * var(--k)) / calc(18px * var(--k)) var(--font-sans);
  letter-spacing: -0.01em;
}
.lang__button img:first-child { width: calc(14px * var(--k)); height: calc(14px * var(--k)); margin: 0.8px; }
.lang__chevron {
  width: calc(8px * var(--k));
  height: calc(4.4px * var(--k));
  margin: calc(5px * var(--k)) calc(3px * var(--k));
  transition: transform 0.3s ease;
}
.lang.is-open .lang__chevron { transform: rotate(180deg); }
.lang__menu {
  position: absolute;
  right: -4px;
  top: calc(100% + 10px);
  min-width: 132px;
  padding: 6px;
  border: 0.6px solid var(--line);
  border-radius: 10px;
  background: var(--ivory);
  box-shadow: 0 12px 28px rgba(67, 47, 50, 0.14);
  animation: menu-in 0.25s cubic-bezier(0.22, 0.61, 0.36, 1);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-6px); } }
.lang__menu button {
  width: 100%;
  padding: 8px 10px;
  border-radius: 6px;
  text-align: left;
  font: 500 16px/20px var(--font-serif);
  letter-spacing: -0.01em;
  transition: background 0.2s ease;
}
.lang__menu button[aria-selected="true"] { background: rgba(107, 48, 63, 0.09); }
@media (hover: hover) { .lang__menu button:hover { background: rgba(107, 48, 63, 0.06); } }

.sound { position: relative; width: calc(24px * var(--k)); height: calc(24px * var(--k)); }
.sound img { position: absolute; inset: 0; width: 100%; height: 100%; transition: opacity 0.25s ease; }
.sound__on { opacity: 0; }
.sound[aria-pressed="true"] .sound__on { opacity: 1; }
.sound[aria-pressed="true"] .sound__off { opacity: 0; }
.sound.is-empty { opacity: 0.4; cursor: default; }
.sound:active { transform: scale(0.94); }

.hero__names, .hero__details {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.hero__names { gap: 6px; }
.hero__details { gap: 12px; }
.names { display: flex; flex-direction: column; align-items: center; }
.names .t-name:first-child, .names .t-amp { margin-bottom: -8px; }

.is-animated .hero__names > *, .is-animated .hero__details > * { opacity: 0; translate: 0 12px; }
.is-animated.is-revealed .hero__names > *, .is-animated.is-revealed .hero__details > * {
  opacity: 1;
  translate: none;
  transition: opacity 1s ease var(--in), translate 1s cubic-bezier(0.22, 0.61, 0.36, 1) var(--in);
}
.hero__names > :nth-child(1) { --in: 0.5s; }
.hero__names > :nth-child(2) { --in: 0.7s; }
.hero__details > :nth-child(1) { --in: 1s; }
.hero__details > :nth-child(2) { --in: 1.15s; }
.hero__details > :nth-child(3) { --in: 1.3s; }

.hero__fade {
  position: absolute;
  left: -1.5px;
  bottom: 0;
  width: 396px;
  height: 169px;
  background: linear-gradient(180deg, rgba(247, 241, 231, 0) 0%, rgba(247, 241, 231, 0.91) 60%, var(--ivory) 100%);
}
.scroll-down {
  position: absolute;
  left: 1px;
  bottom: 0;
  width: 393px;
  height: 78px;
  padding-bottom: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.scroll-down__arrows { position: relative; width: 14px; height: 17px; }
.scroll-down__arrows img { position: absolute; left: 0; top: 0; }
.scroll-down__arrows img + img { top: 7px; }
.scroll-down__arrows img { animation: arrow-drift 2.2s ease-in-out infinite; }
.scroll-down__arrows img + img { animation-delay: 0.15s; }
@keyframes arrow-drift {
  0%, 100% { transform: translateY(0); opacity: 1; }
  50% { transform: translateY(4px); opacity: 0.55; }
}

/* ---------- 2. Our letter ---------- */
.letter {
  position: absolute;
  left: 24px;
  top: 112px;
  width: 345px;
  height: 625px;
}
.letter__lace {
  position: absolute;
  left: -20px;
  top: -12px;
  width: 385px;
  height: 650px;
  object-fit: cover;
}
.letter__bow { position: absolute; left: 145px; top: 83px; width: 55px; height: 30.25px; }
.letter__content {
  position: absolute;
  left: 50.5px;
  top: calc(50% + 0.5px);
  width: 244px;
  translate: 0 -50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
}
.letter__text { width: 212px; }

.divider {
  position: absolute;
  left: 133.5px;
  top: 760px;
  width: 126px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font: 500 14px/18px var(--font-serif);
}
.divider i { width: 42px; height: 0.6px; background: var(--wine); }
.divider span { width: 18px; text-align: center; }

/* ---------- 3. Save the date ---------- */
.envelope {
  position: absolute;
  left: 0;
  top: 170px;
  width: 393px;
  height: 393px;
}
.envelope__art {
  position: absolute;
  inset: 0;
  background: url("../assets/img/envelope.jpg") 0 0 / 200% 100% no-repeat;
  transition: opacity 0.6s ease;
}
.envelope__art--open { background-position: 100% 0; opacity: 0; }
.envelope__card {
  position: absolute;
  left: 95px;
  top: 98px;
  width: 203px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: center;
  opacity: 0;
  translate: 0 14px;
}
.envelope.is-open .envelope__art--open { opacity: 1; }
.envelope.is-open .envelope__art--closed { opacity: 0; }
.envelope.is-open .envelope__card {
  opacity: 1;
  translate: none;
  transition: opacity 0.8s ease 0.35s, translate 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) 0.35s;
}
.envelope:not(.is-open):active { transform: scale(0.99); }
/* A small nudge so the closed envelope reads as something to tap. */
.envelope:not(.is-open) { animation: envelope-nudge 4s ease-in-out 1.5s infinite; }
@keyframes envelope-nudge {
  0%, 84%, 100% { transform: none; }
  88% { transform: rotate(-1deg); }
  92% { transform: rotate(1deg); }
  96% { transform: rotate(-0.5deg); }
}

.envelope__hint { transition: opacity 0.5s ease; }
.envelope__hint--open { opacity: 0; }
.screen--date.is-open .envelope__hint--closed { opacity: 0; }
.screen--date.is-open .envelope__hint--open { opacity: 1; transition-delay: 0.4s; }

/* ---------- 4. The celebration ---------- */
/* The programme screen sticks to the viewport while the thread does its work; main.js sets both
   the height of the track it scrolls through and the height of the sticky part. */
.screen--pin {
  overflow: visible;
  height: calc(var(--vh) + var(--pin-run, 0px));
}
/* The programme's own board is only as tall as its contents, so it can be scaled to fit the
   pinned screen without the timeline being cut off at the bottom. */
.screen--pin .stage { height: 780px; }
.screen--pin .pin {
  position: sticky;
  top: 0;
  height: var(--vh);
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* Track of the thread. Its fill is as long as the scroll has pulled it (--p, set by main.js). */
.timeline__ribbon {
  position: absolute;
  left: 169.5px;
  top: 253px;
  width: 1px;
  height: 493px;
}
.timeline__ribbon i {
  display: block;
  width: 100%;
  height: calc(100% * var(--p, 1));
  background: var(--wine);
}

.timeline {
  position: absolute;
  left: 32px;
  top: 226px;
  width: 329px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.moment {
  position: relative;
  height: 100px;
  display: grid;
  grid-template-columns: 110px 24px 163px;
  column-gap: 16px;
}
.moment__icon { margin: 18px 0 0 23px; width: 64px; height: 64px; }
.moment__marker {
  margin-top: 15px;
  width: 24px;
  height: 40px;
  /* Until main.js swaps in the inline drawing, the flat icon stands in. */
  background: url("../assets/icons/ribbon-marker.svg") center / contain no-repeat;
}
.moment__marker.is-drawn { background: none; }
.moment__marker svg { width: 24px; height: 40px; overflow: visible; }
/* The icon and the words arrive as the bow beside them is tied (main.js adds .is-in). */
.is-animated .moment__icon,
.is-animated .moment__text {
  opacity: 0;
  translate: 0 14px;
}
.is-animated .moment.is-in .moment__icon,
.is-animated .moment.is-in .moment__text {
  opacity: 1;
  translate: none;
  transition: opacity 0.7s ease, translate 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.moment__text {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-top: 14px;
  font: 500 22px/26px var(--font-serif);
  letter-spacing: -0.01em;
}
.t-time { font-style: italic; color: var(--wine); }
.t-event { color: var(--ink); }

/* ---------- 5. The venue ---------- */
.venue { position: absolute; left: 0; top: 0; width: 393px; height: 560px; pointer-events: none; }
.venue__frame {
  position: absolute;
  left: 4px;
  top: 206px;
  width: 385px;
  height: 342px;
  object-fit: cover;
}
.venue__window {
  position: absolute;
  left: 59px;
  top: 258px;
  width: 275px;
  height: 243px;
  overflow: hidden;
  border-radius: 137.5px 137.5px 0 0;
  background: var(--paper);
}
.venue__window img {
  position: absolute;
  left: -56px;
  top: -336px;
  width: 387px;
  height: 580.5px;
  object-fit: cover;
}
.venue__details {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 11px;
  text-align: center;
}
.actions { display: flex; gap: 11px; }

/* The date, spelled out where the Calendar button can act on it. */
.venue__date {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
.venue__date i { flex: 1; height: 0.6px; background: var(--line); }
.venue__date span { display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center; }
.venue__date em { font-style: normal; color: var(--muted); }
.t-date-22 { font: 500 24px/28px var(--font-serif); letter-spacing: 0.01em; color: var(--wine); }

/* ---------- 6. Dress code ---------- */
.attire { display: flex; flex-direction: column; gap: 18px; text-align: center; }
.palette { display: flex; gap: 20px; }
.palette li {
  width: 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 13px;
  font: 400 10px/14px var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--muted);
  text-align: center;
}
/* A swatch is a plain circle of colour — the couple's own palette drops straight into the
   --swatch values in the markup, with no artwork to prepare. */
.swatch {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--swatch, var(--line));
  box-shadow: inset 0 0 0 0.6px rgba(67, 47, 50, 0.12);
}
.palette li:nth-child(2) { --in: 0.1s; }
.palette li:nth-child(3) { --in: 0.2s; }
.palette li:nth-child(4) { --in: 0.3s; }
.guests { height: 276px; object-fit: contain; mix-blend-mode: multiply; }

/* ---------- 7. RSVP ---------- */
.rsvp {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 63px;
  padding: 48px 24px;
  color: var(--paper);
}
.rsvp__heading { width: 329px; display: flex; flex-direction: column; gap: 20px; text-align: center; }

.reply-card { position: relative; width: 345px; height: 527px; flex-shrink: 0; }
.reply-card__lace {
  position: absolute;
  left: -1px;
  top: -11.8px;
  width: 345px;
  height: 560.6px;
}
.reply {
  position: absolute;
  left: 24.5px;
  top: calc(50% - 1.5px);
  translate: 0 -50%;
  width: 293px;
  padding: 20px;
  border-radius: 16px;
  background: var(--wine);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
.field { width: 257px; display: flex; flex-direction: column; gap: 8px; }
fieldset.field { gap: 10px; }
fieldset.field legend { margin-bottom: 10px; }

.pill {
  width: 257px;
  height: 40px;
  padding: 0 14px;
  border: 0.7px solid rgba(247, 241, 231, 0.55);
  border-radius: 20px;
  background: none;
  color: var(--paper);
  font: 400 14px/18px var(--font-sans);
  letter-spacing: -0.01em;
  outline: none;
  transition: border-color 0.2s ease;
}
.pill::placeholder { color: rgba(247, 241, 231, 0.7); }
.pill:focus, .pill:focus-within { border-color: var(--paper); }
.pill--area { height: 72px; padding-top: 12px; border-radius: 12px; resize: none; }
.field.is-invalid .pill, .field.is-invalid .choice { border-color: #f0b8b8; }

.choice { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice__dot {
  position: relative;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border: 1px solid var(--paper);
  border-radius: 50%;
}
.choice__dot::after {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: var(--paper);
  transform: scale(0);
  transition: transform 0.2s ease;
}
.choice:has(input:checked) { border: 1px solid var(--paper); }
.choice:has(input:checked) .choice__dot::after { transform: none; }
.choice:has(input:focus-visible) { outline: 1px dashed rgba(247, 241, 231, 0.6); outline-offset: 3px; }

.reply .btn { margin-top: 0; }

#rsvp.is-sent { background: var(--paper); }
.thanks__text { display: flex; flex-direction: column; align-items: center; gap: 28px; text-align: center; }
.thanks.is-in > * { animation: fade-up 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) both; }
.thanks.is-in > :nth-child(2) { animation-delay: 0.15s; }
.thanks.is-in > :nth-child(3) { animation-delay: 0.3s; }
@keyframes fade-up { from { opacity: 0; transform: translateY(14px); } }

/* ---------- 8. With love ---------- */
.oval-frame { height: 593px; object-fit: cover; }
.scratch {
  position: absolute;
  touch-action: none;
  left: 84.5px;
  top: 173px;
  width: 224px;
  height: 435px;
  border-radius: 50%;
  overflow: hidden;
}
.scratch__photo, .scratch__cover { position: absolute; inset: 0; width: 100%; height: 100%; }
.scratch__cover { touch-action: none; cursor: grab; transition: opacity 0.8s ease; }
.scratch__hint {
  position: absolute;
  left: 12px;
  top: 210px;
  width: 200px;
  font: 400 11px/15px var(--font-sans);
  letter-spacing: -0.01em;
  text-align: center;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.scratch.is-scratching .scratch__hint,
.scratch.is-clear .scratch__hint { opacity: 0; }
.scratch.is-clear .scratch__cover { opacity: 0; pointer-events: none; }

/* Footer bar, like the letter site's: logo on the left, back to the top on the right. It hangs off
   the screen rather than the board, so it stays at the bottom of the window. */
.footer {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: min(100%, calc(393px * var(--k)));
  translate: -50% 0;
  height: calc(72px * var(--k));
  padding: 0 calc(24px * var(--k));
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--paper);
  border-top: 0.6px solid rgba(207, 186, 176, 0.55);
}
.footer__logo { width: calc(103px * var(--k)); height: calc(24px * var(--k)); }
.to-top {
  display: grid;
  place-items: center;
  width: calc(40px * var(--k));
  height: calc(40px * var(--k));
  border: 0.6px solid var(--line);
  border-radius: 50%;
  font: 500 calc(18px * var(--k)) / 1 var(--font-serif);
  color: var(--wine);
  transition: background 0.25s ease, transform 0.25s ease;
}
.to-top:active { transform: translateY(-2px); }
@media (hover: hover) { .to-top:hover { background: rgba(107, 48, 63, 0.06); } }

/* ---------- Sheets (calendar / map) ---------- */
.sheet { position: fixed; inset: 0; z-index: 40; }
.sheet__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(67, 47, 50, 0.4);
  opacity: 0;
  transition: opacity 0.35s ease;
}
.sheet__panel {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: min(100%, calc(393px * var(--k)));
  background: var(--ivory);
  translate: -50% 100%;
  transition: translate 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
  padding-bottom: env(safe-area-inset-bottom);
}
.sheet__panel .stage { height: 490px; }
.sheet.is-open .sheet__backdrop { opacity: 1; }
.sheet.is-open .sheet__panel { translate: -50% 0; }
.sheet__close {
  position: absolute;
  left: 337px;
  top: 12px;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
}
.sheet .t-month { font-size: 24px; line-height: 28px; }

.calendar {
  position: absolute;
  left: 42px;
  top: 132px;
  width: 309px;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-auto-rows: 29px;
  row-gap: 17px;
  text-align: center;
  font: 500 18px/22px var(--font-serif);
  letter-spacing: -0.01em;
}
.calendar > * { padding-top: 3px; }
.calendar__dow {
  height: 16px;
  padding: 0;
  font: 500 12px/16px var(--font-sans);
  align-self: start;
}
.calendar__day {
  position: relative;
  z-index: 0;
  color: var(--ivory);
  font: inherit;
}
.calendar__day::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: calc(50% + 0.23px);
  top: calc(50% + 1.5px);
  width: 31.67px;
  height: 28.5px;
  translate: -50% -50%;
  background: url("../assets/icons/heart.svg") center / contain no-repeat;
  animation: heartbeat 1.8s ease-in-out infinite;
}
@keyframes heartbeat {
  0%, 60%, 100% { transform: scale(1); }
  20% { transform: scale(1.12); }
  40% { transform: scale(0.98); }
}

.map {
  position: absolute;
  left: 44px;
  top: 113px;
  width: 305px;
  height: 267px;
  overflow: hidden;
  border: 0.8px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
}
.map__art { position: absolute; left: -0.8px; top: -0.8px; width: 305px; height: 267px; }
.map__label {
  position: absolute;
  left: 188.2px;
  top: 159.2px;
  translate: -50% 0;
  font: 400 10px/14px var(--font-sans);
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.map__pin {
  position: absolute;
  left: calc(221px + 2.25px);
  top: calc(245px + 1.25px);
  width: 19.5px;
  height: 21.5px;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition-duration: 0.01s !important; transition-delay: 0s !important; }
}
