/* ==========================================================================
   Mayank & Shreya — wedding invitation
   Sections follow the order of index.html.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Design tokens
   -------------------------------------------------------------------------- */
:root {
  --radius: .625rem;

  --background: oklch(96% .015 80);
  --foreground: oklch(25% .04 45);
  --card: oklch(94% .015 80);
  --primary: oklch(45% .12 15);
  --primary-foreground: oklch(96% .015 80);
  --muted-foreground: oklch(55% .05 45);
  --accent: oklch(72% .14 80);
  --border: oklch(85% .02 80);
  --gold: oklch(72% .14 80);
  --gold-light: oklch(86% .1 85);

  --rose-300: oklch(81% .117 11.638);
  --rose-400: oklch(71.2% .194 13.428);
  --rose-600: oklch(58.6% .253 17.585);
  --red-500: oklch(63.7% .237 25.331);

  --font-serif: "Playfair Display", ui-serif, Georgia, serif;
  --font-script: "Great Vibes", cursive;

  --shadow-sm: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a;
  --shadow-lg: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
  --shadow-2xl: 0 25px 50px -12px #00000040;
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-serif);
  line-height: 1.5;
  overflow-x: hidden;
}

img, video { display: block; max-width: 100%; }
iframe { display: block; border: 0; }

/* Several overlays below set `display`, which would otherwise beat the
   browser's own rule for the `hidden` attribute. */
[hidden] { display: none !important; }

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

h1, h2, p { margin: 0; }

/* Shared text styles ------------------------------------------------------- */
.eyebrow {
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .3em;
  color: var(--muted-foreground);
}

.eyebrow--wide {
  font-size: .75rem;
  line-height: 1.3333;
  font-weight: 400;
  letter-spacing: .35em;
}

.section-head {
  position: relative;
  z-index: 10;
  margin: 0 auto;
  max-width: 72rem;
  padding: 0 1.5rem;
  text-align: center;
}

.section-head .eyebrow { margin-bottom: .75rem; }

.section-title {
  font-family: var(--font-script);
  font-size: 2.25rem;
  line-height: 1.1111;
  font-weight: 400;
  color: var(--primary);
}

/* Hides the scrollbar while keeping the element scrollable. */
.no-scrollbar { scrollbar-width: none; -ms-overflow-style: none; }
.no-scrollbar::-webkit-scrollbar { display: none; }

@media (min-width: 768px) {
  .section-title { font-size: 3.75rem; line-height: 1; }
}

/* --------------------------------------------------------------------------
   Particles — floating hearts, the click burst, falling petals
   -------------------------------------------------------------------------- */
.hearts {
  pointer-events: none;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.hearts--fixed { position: fixed; }
.hearts--absolute { position: absolute; }

.hearts__item {
  position: absolute;
  bottom: -2rem;
  color: var(--rose-400);
  animation: float-heart var(--duration) linear var(--delay) infinite;
}

.hearts__item svg { width: 1em; height: 1em; fill: currentColor; }

.burst {
  pointer-events: none;
  position: fixed;
  inset: 0;
  z-index: 50;
  overflow: hidden;
}

.burst__item {
  position: absolute;
  bottom: 33.333333%;
  animation: bubble-rise var(--duration) ease-out var(--delay) forwards;
}

.burst__item svg { width: 1em; height: 1em; fill: currentColor; }
.burst__item--gold { color: var(--gold); }
.burst__item--rose { color: var(--rose-300); }
.burst__item--cream { color: var(--primary-foreground); }

.petals {
  pointer-events: none;
  position: fixed;
  inset: 0;
  z-index: 20;
  overflow: hidden;
}

.petals__item {
  position: absolute;
  top: -2.5rem;
  display: block;
  border-radius: 60% 0 60% 0;
  animation: petal-fall var(--duration) linear var(--delay) infinite;
}

.petals__item--rose { background: color-mix(in oklab, var(--rose-600) 80%, transparent); }
.petals__item--red  { background: color-mix(in oklab, var(--red-500) 75%, transparent); }

/* --------------------------------------------------------------------------
   0a. Sealed page — held still until the invitation is opened

   The door and the film are fixed overlays, so the invitation behind them is
   out of sight — but it is still a full-length page, and a swipe or a wheel
   would scroll it. The guest sees nothing move, then the film fades and hands
   them an invitation already halfway down, past the names they were meant to
   read first.

   So the page is pinned for as long as the overlays are up. The class is set
   on <html> by an inline script in index.html — written into the markup it
   would trap a guest whose scripts never arrive — and lifted in main.js the
   moment the invitation is theirs.
   -------------------------------------------------------------------------- */
.is-sealed,
.is-sealed body {
  height: 100%;
  overflow: hidden;
  /* Stops the rubber-band drag Safari otherwise still allows. */
  overscroll-behavior: none;
}

/* Hiding the scrollbar narrows the page by its width, so the door would jog
   sideways as the seal lifts. Keep its place reserved. The document is the
   only scroller here, so this belongs on <html> alone. */
.is-sealed { scrollbar-gutter: stable; }

/* iOS below 16 scrolls the document anyway, whatever the rule above says.
   Refusing the gesture where it starts is the part it does honour. Taps are
   unaffected, so the invite button and Skip still work. */
.door,
.intro { touch-action: none; }

/* --------------------------------------------------------------------------
   1. Opening door
   -------------------------------------------------------------------------- */
.door {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  background: oklch(100% 0 0);
  color: var(--foreground);
}

/* Added the moment the invite button is pressed. */
.door.is-opening {
  pointer-events: none;
  animation: curtain-reveal 1.2s cubic-bezier(.22, 1, .36, 1) forwards;
}

.door__bg { position: absolute; inset: 0; }

.door__bg img { width: 100%; height: 100%; object-fit: cover; }

.door__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, #00000073, transparent 50%, transparent);
}

.door__cta {
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  padding-bottom: 12vh;
}

.invite-btn {
  position: relative;
  display: flex;
  align-items: center;
  gap: .75rem;
  overflow: hidden;
  padding: 1.25rem 2.5rem;
  border: 2px solid var(--gold);
  border-radius: 9999px;
  background: var(--primary);
  color: var(--primary-foreground);
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: .025em;
  box-shadow: var(--shadow-2xl);
  transition: transform .15s cubic-bezier(.4, 0, .2, 1);
  animation: pulse-glow 2s ease-in-out infinite;
}

.invite-btn:hover { transform: scale(1.05); }
.invite-btn:active { transform: scale(.95); }

.invite-btn span { position: relative; z-index: 10; }

.invite-btn__heart {
  position: relative;
  z-index: 10;
  width: 1.5rem;
  height: 1.5rem;
  fill: currentColor;
  transition: transform .15s cubic-bezier(.4, 0, .2, 1);
}

.invite-btn:hover .invite-btn__heart { transform: scale(1.25); }

/* --------------------------------------------------------------------------
   2. Intro video
   -------------------------------------------------------------------------- */
.intro {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
  transition: opacity 1200ms cubic-bezier(.4, 0, .2, 1);
}

.intro.is-fading { pointer-events: none; opacity: 0; }

.intro video { width: 100%; height: 100%; object-fit: cover; }

/* Shown while the film is arriving, and again if it runs out of buffer
   mid-scene. The percentage matters more than the spinner: a number that
   climbs tells a guest on a slow connection that the page is working, where a
   spinner alone reads as a page that has hung. */
.intro__loader {
  pointer-events: none;
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  align-items: center;
  justify-content: center;
}

.intro__spinner { display: flex; }

.intro__spinner span {
  width: 2.5rem;
  height: 2.5rem;
  border: 2px solid color-mix(in oklab, var(--gold) 40%, transparent);
  border-top-color: var(--gold);
  border-radius: 9999px;
  animation: spin 1s linear infinite;
}

.intro__progress {
  margin: 0;
  font-size: .82rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--gold) 85%, white);
  font-variant-numeric: tabular-nums;
}

/* The way out. Hidden until waiting stops feeling instantaneous — offered
   straight away it would suggest the film was not worth watching. */
.intro__skip {
  position: absolute;
  right: 1.25rem;
  bottom: 1.5rem;
  z-index: 2;
  padding: .5rem 1.1rem;
  border: 1px solid color-mix(in oklab, var(--gold) 55%, transparent);
  border-radius: 9999px;
  background: rgba(0, 0, 0, .35);
  color: color-mix(in oklab, var(--gold) 90%, white);
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background .25s ease, border-color .25s ease;
}

.intro__skip:hover {
  background: rgba(0, 0, 0, .55);
  border-color: var(--gold);
}

/* --------------------------------------------------------------------------
   2a. Background music toggle — fixed bottom-right for the whole visit; sits
   under the door and intro (both z-index 50) so they still cover it, then
   is revealed once the invitation is on screen.
   -------------------------------------------------------------------------- */
.music-toggle {
  position: fixed;
  bottom: 1.1rem;
  right: 1.1rem;
  z-index: 45;
  display: grid;
  place-items: center;
  width: 2.85rem;
  height: 2.85rem;
  border: 2px solid var(--gold);
  border-radius: 9999px;
  background: var(--primary);
  color: var(--primary-foreground);
  box-shadow: var(--shadow-lg);
  transition: transform .15s cubic-bezier(.4, 0, .2, 1), box-shadow .2s ease;
}

.music-toggle:hover { transform: scale(1.08); box-shadow: var(--shadow-2xl); }
.music-toggle:active { transform: scale(.92); }

.music-toggle__ring {
  position: absolute;
  inset: -6px;
  border-radius: 9999px;
  border: 1px solid color-mix(in oklab, var(--gold-light) 70%, transparent);
  opacity: 0;
  animation: music-ring 2.4s ease-out infinite;
}

.music-toggle:not(.is-muted) .music-toggle__ring { opacity: 1; }

.music-toggle__disc {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  animation: music-spin 3.2s linear infinite;
  animation-play-state: paused;
}

.music-toggle:not(.is-muted) .music-toggle__disc { animation-play-state: running; }

.music-toggle__icon { width: 1.3rem; height: 1.3rem; transition: opacity .2s ease; }

.music-toggle__slash {
  position: absolute;
  z-index: 1;
  width: 1.3rem;
  height: 1.3rem;
  opacity: 0;
  transform: scale(.5) rotate(-15deg);
  transition: opacity .2s ease, transform .2s cubic-bezier(.34, 1.56, .64, 1);
}

.music-toggle.is-muted .music-toggle__slash { opacity: 1; transform: scale(1) rotate(0deg); }
.music-toggle.is-muted .music-toggle__disc { opacity: .5; }

@keyframes music-ring {
  0%   { scale: .85; opacity: .65; }
  100% { scale: 1.4;  opacity: 0; }
}

@keyframes music-spin { to { transform: rotate(360deg); } }

@media (min-width: 640px) {
  .music-toggle { bottom: 1.5rem; right: 1.5rem; width: 3.1rem; height: 3.1rem; }
}

/* --------------------------------------------------------------------------
   3. The invitation
   -------------------------------------------------------------------------- */
.invite {
  position: relative;
  z-index: 10;
  /* The glows below sit outside this box; without this they widen the page
     and push every section off-centre on narrow screens. */
  overflow: hidden;
  display: flex;
  min-height: 100vh;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 4rem 1.5rem;
  text-align: center;
  opacity: 0;
  transition: opacity 1s cubic-bezier(.4, 0, .2, 1);
}

.invite.is-visible { opacity: 1; }

/* One small floral cutout is mirrored at the edges. The invitation copy stays
   above it, and the image waits for the opening tap before it is requested. */
.invite__florals {
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 1;
  width: min(100%, 80rem);
  margin: 0 auto;
}

.invite__floral {
  position: absolute;
  top: -.75rem;
  width: clamp(5.25rem, 14vw, 13rem);
  opacity: 0;
  transition: opacity 1.1s ease-out, transform 1.1s ease-out;
}

.invite__floral img {
  width: 100%;
  height: auto;
  transform-origin: 18% 3%;
  animation: invite-floral-wind 5.8s ease-in-out infinite;
  animation-play-state: paused;
}

.invite__floral--right img { animation-delay: -2.3s; }

.invite__floral--left {
  left: -1.5rem;
  transform: translateY(-.75rem);
}

.invite__floral--right {
  right: -1.5rem;
  transform: translateY(-.75rem) scaleX(-1);
}

.invite.is-visible .invite__floral { opacity: .78; }
.invite.is-visible .invite__floral--left { transform: translateY(0); }
.invite.is-visible .invite__floral--right { transform: translateY(0) scaleX(-1); }
.invite.is-visible.has-floral-motion .invite__floral img {
  animation-play-state: running;
  will-change: transform;
}

@keyframes invite-floral-wind {
  0%, 100% { transform: rotate(0) translateX(0); }
  18% { transform: rotate(1.2deg) translateX(1px); }
  36% { transform: rotate(2.6deg) translateX(3px); }
  58% { transform: rotate(.4deg) translateX(1px); }
  77% { transform: rotate(-1.3deg) translateX(-1px); }
}

@media (max-width: 479px) {
  .invite__floral { width: 5rem; }
  .invite.is-visible .invite__floral { opacity: .65; }
}

/* Shown after the invitation text has finished revealing, then removed as
   soon as the guest follows it. */
.invite__scroll {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-top: 1.5rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity .6s ease-out;
}

.invite__scroll.is-visible { opacity: 1; }

.invite__scroll-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .1rem;
  animation: scroll-hint 1s cubic-bezier(.4, 0, .2, 1) infinite;
}

.invite__scroll-text {
  font-size: clamp(.75rem, 2.8vw, 1rem);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold);
  text-shadow: 0 0 10px color-mix(in oklab, var(--gold) 55%, transparent);
}

.invite__scroll-arrow {
  font-size: clamp(1.8rem, 7vw, 2.8rem);
  line-height: .8;
  color: var(--primary);
}

@keyframes scroll-hint {
  0%, 100% { opacity: .8; transform: translateY(0); }
  50%      { opacity: 1; transform: translateY(8px); }
}

.invite__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 48rem;
}

.invite__glow {
  position: absolute;
  border-radius: 9999px;
  filter: blur(64px);
}

.invite__glow--gold {
  left: -4rem;
  top: -4rem;
  width: 10rem;
  height: 10rem;
  background: color-mix(in oklab, var(--gold) 10%, transparent);
}

.invite__glow--rose {
  right: -4rem;
  bottom: -4rem;
  width: 12rem;
  height: 12rem;
  background: color-mix(in oklab, var(--primary) 10%, transparent);
}

.invite__stack { position: relative; }
.invite__stack > * + * { margin-top: 2rem; }
.invite__stack > .reveal:nth-child(5),
.invite__stack > .reveal:nth-child(6) { margin-top: 1rem; }

/* Each block blurs and slides into place, staggered by an inline delay. The
   run happens behind the transparent .invite, which then fades the lot in. */
.reveal {
  opacity: 0;
  animation: text-reveal .45s cubic-bezier(.22, 1, .36, 1) forwards;
}

.name {
  font-family: var(--font-script);
  font-size: 4.5rem;
  line-height: 1;
  font-weight: 400;
  color: var(--primary);
}

.parents {
  margin-top: .75rem;
  font-size: .875rem;
  line-height: 1.4286;
  font-weight: 300;
  letter-spacing: .025em;
  color: var(--muted-foreground);
}

.ampersand {
  font-family: var(--font-script);
  font-size: 3.75rem;
  line-height: 1;
  color: var(--accent);
  animation: soft-float 4s ease-in-out infinite;
}

.divider {
  margin: 0 auto;
  height: 1px;
  width: 8rem;
  background: linear-gradient(to right, transparent, var(--border), transparent);
}

.knot {
  font-size: 1.25rem;
  line-height: 1.4;
  font-weight: 500;
}

.knot span { color: var(--accent); }

.countdown {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: .75rem;
  margin: 0 auto;
  max-width: 32rem;
  padding-top: 1rem;
}

.countdown__cell {
  padding: .75rem;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) + 4px);
  background: color-mix(in oklab, var(--card) 80%, transparent);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.countdown__value {
  font-size: 1.5rem;
  line-height: 1.3333;
  font-weight: 700;
  color: var(--foreground);
}

.countdown__label {
  margin-top: .25rem;
  font-size: .625rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted-foreground);
}

@media (min-width: 768px) {
  .name { font-size: 8rem; }
  .parents { font-size: 1rem; line-height: 1.5; }
  .ampersand { font-size: 6rem; }
  .knot { font-size: 1.5rem; line-height: 1.3333; }
  .countdown__value { font-size: 2.25rem; line-height: 1.1111; }
  .countdown__label { font-size: .75rem; line-height: 1.3333; }
}

/* --------------------------------------------------------------------------
   4. Save the date
   -------------------------------------------------------------------------- */
.save-date {
  position: relative;
  min-height: 100vh;
  overflow: hidden;
}

.save-date__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.save-date__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in oklab, var(--foreground) 40%, transparent);
}

.save-date__inner {
  position: relative;
  z-index: 10;
  display: flex;
  min-height: 100vh;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2.5rem;
  padding: 4rem 1.5rem;
  text-align: center;
}

.save-date__eyebrow {
  font-size: .875rem;
  line-height: 1.4286;
  text-transform: uppercase;
  letter-spacing: .35em;
  color: var(--gold-light);
}

.save-date__names {
  margin-top: .75rem;
  font-family: var(--font-script);
  font-size: 3.75rem;
  line-height: 1;
  font-weight: 400;
  color: var(--background);
  filter: drop-shadow(0 4px 4px #00000026);
}

.save-date__hint {
  font-size: .875rem;
  line-height: 1.4286;
  text-transform: uppercase;
  letter-spacing: .25em;
  color: color-mix(in oklab, var(--background) 80%, transparent);
}

.scratch {
  position: relative;
  height: 14rem;
  width: min(90vw, 26rem);
  overflow: hidden;
  border: 2px solid color-mix(in oklab, var(--gold-light) 80%, transparent);
  border-radius: calc(var(--radius) + 12px);
  box-shadow: var(--shadow-2xl);
}

.scratch__prize {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  background: var(--card);
}

.scratch__label {
  font-size: .75rem;
  line-height: 1.3333;
  text-transform: uppercase;
  letter-spacing: .3em;
  color: var(--muted-foreground);
}

.scratch__date {
  font-family: var(--font-script);
  font-size: 3rem;
  line-height: 1;
  color: var(--primary);
}

.scratch__year {
  font-size: 1.5rem;
  line-height: 1.3333;
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--accent);
}

.scratch__foil {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  touch-action: none;
  transition: opacity .7s cubic-bezier(.4, 0, .2, 1);
}

.scratch.is-revealed .scratch__foil { pointer-events: none; opacity: 0; }

/* The hand that shows what to do with the card.

   Sits over the foil and sweeps left to right, which is the one instruction
   nobody has to read. It is up whenever the card has been left untouched and
   gone the moment a finger lands — see assets/js/scratch-card.js, which also
   keeps it off screen entirely until the guest has scrolled this far.

   `pointer-events: none` throughout: the hand is a picture of a gesture, and
   must never intercept the real one. */
.scratch__hand {
  pointer-events: none;
  position: absolute;
  left: 50%;
  top: 58%;
  z-index: 2;
  width: 2.75rem;
  height: 2.75rem;
  margin-left: -1.375rem;
  color: color-mix(in oklab, var(--foreground) 75%, white);
  animation: scratch-swipe 2.6s cubic-bezier(.45, 0, .55, 1) infinite;
}

/* A soft halo so the hand stays legible over both the light and dark bands of
   the foil gradient. */
.scratch__hand::before {
  content: '';
  position: absolute;
  inset: -50%;
  border-radius: 9999px;
  background: radial-gradient(circle, #ffffffd9, #ffffff00 68%);
}

.scratch__hand svg {
  position: relative;
  width: 100%;
  height: 100%;
  filter: drop-shadow(0 2px 3px #00000040);
}

/* Held briefly at each end so the sweep reads as a repeated gesture rather
   than a loop that never stops moving. */
@keyframes scratch-swipe {
  0%, 10%   { transform: translateX(-6.5rem) rotate(-12deg); opacity: 0; }
  22%       { opacity: 1; }
  78%       { opacity: 1; }
  90%, 100% { transform: translateX(6.5rem) rotate(12deg); opacity: 0; }
}

@media (min-width: 768px) {
  .save-date__names { font-size: 6rem; }
  .scratch__date { font-size: 3.75rem; }
}

/* --------------------------------------------------------------------------
   5. Memories carousel
   -------------------------------------------------------------------------- */
.memories {
  position: relative;
  overflow: hidden;
  background: var(--background);
  padding: 5rem 0 2rem;
}

.memories__track {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 3.5rem;
  padding: 1rem 30vw 2.5rem;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  touch-action: pan-x pan-y;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.memories__track::-webkit-scrollbar { display: none; }

.memories__item {
  position: relative;
  flex-shrink: 0;
  width: 40vw;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: calc(var(--radius) + 8px);
  box-shadow: 0 25px 60px -18px rgba(0, 0, 0, .45),
              0 0 0 1px color-mix(in oklab, var(--gold) 50%, transparent);
  cursor: pointer;
  transform-origin: center;
  scroll-snap-align: center;
  transition: transform .2s cubic-bezier(0, 0, .2, 1), opacity .2s cubic-bezier(0, 0, .2, 1);
  will-change: transform;
}

.memories__item img { width: 100%; height: 100%; object-fit: cover; }

.memories__hint {
  position: relative;
  z-index: 10;
  margin-top: -.5rem;
  text-align: center;
  font-size: .75rem;
  line-height: 1.3333;
  text-transform: uppercase;
  letter-spacing: .25em;
  color: var(--muted-foreground);
}

@media (min-width: 768px) {
  .memories__track { gap: 1.5rem; padding-left: 36vw; padding-right: 36vw; }
  .memories__item { width: 22rem; }
}

/* --------------------------------------------------------------------------
   6. Wedding functions
   -------------------------------------------------------------------------- */
.functions {
  position: relative;
  overflow: hidden;
  background: var(--background);
  padding: 5rem 0;
}

.functions__viewport { position: relative; margin-top: 3rem; }

.functions__track {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2.5rem;
  padding: 0 1.5rem;
}

.function {
  margin: 0;
  width: 100%;
  max-width: 24rem;
  flex-shrink: 0;
}

.function__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  border-radius: calc(var(--radius) + 12px);
  box-shadow: 0 30px 70px -25px rgba(0, 0, 0, .45),
              0 0 0 1px color-mix(in oklab, var(--gold) 60%, transparent);
  transition: transform .5s cubic-bezier(.4, 0, .2, 1);
}

/* A card holds this warm ground until its film arrives. The clips are fetched
   one at a time rather than all four at once (assets/js/sections.js), so a
   card genuinely does sit empty for a moment on a slow connection — and an
   empty card should look like part of the invitation, not like a hole in it. */
.function__frame {
  background:
    radial-gradient(120% 100% at 50% 0%,
      color-mix(in oklab, var(--gold) 22%, transparent),
      transparent 60%),
    linear-gradient(160deg,
      color-mix(in oklab, var(--rose-300) 30%, var(--background)),
      var(--background));
}

.function__frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity .6s ease;
}

/* A card with no film loaded yet paints nothing, which over the warm ground
   above would show as a hole. Held back until there are frames to show, so
   the fade-in covers the gap between the element getting a source and having
   something to paint.

   A card given a `poster` is exempt: that is a real still of the scene, and
   showing it immediately is the whole point of having one. */
.function__frame video:not([poster]) { opacity: 0; }
.function__frame.is-playing video { opacity: 1; }

/* Play affordance. Fades away as soon as the film runs, so it is only really
   seen where autoplay does not happen: save-data, and wherever the browser
   refused. There it is the thing that makes the card tappable. */
.function__frame::before,
.function__frame::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 1;
  pointer-events: none;
  opacity: 1;
  transition: opacity .4s ease;
}

.function__frame::before {
  width: 3.4rem;
  height: 3.4rem;
  transform: translate(-50%, -50%);
  border: 1px solid color-mix(in oklab, var(--gold) 60%, transparent);
  border-radius: 9999px;
  background: rgba(0, 0, 0, .28);
}

.function__frame::after {
  width: 0;
  height: 0;
  transform: translate(-30%, -50%);
  border-top: .5rem solid transparent;
  border-bottom: .5rem solid transparent;
  border-left: .85rem solid color-mix(in oklab, var(--gold) 90%, white);
}

.function__frame.is-playing::before,
.function__frame.is-playing::after { opacity: 0; }

/* The text sits over the film itself — centred, and lifted above the
   middle by a top padding that is a share of the card, so it holds its place
   on any card size. The gradient keeps it readable whatever the frame
   underneath is doing. */
.function__caption {
  pointer-events: none;
  position: absolute;
  inset: 0;
  /* Above the play affordance drawn on the frame, which is a pseudo-element
     and would otherwise paint over the words. */
  z-index: 2;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: .875rem;
  padding: 21% 1.5rem 2rem;
  text-align: center;
  color: var(--background);
  text-shadow: 0 2px 14px color-mix(in oklab, var(--foreground) 75%, transparent);
}

/* The scrim rides its own layer so it can fade in with the words, leaving the
   first second of the film unclouded. */
.function__caption::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1s cubic-bezier(.4, 0, .2, 1);
  background:
    radial-gradient(ellipse 80% 40% at 50% 30%,
                color-mix(in oklab, var(--foreground) 62%, transparent) 0%,
                color-mix(in oklab, var(--foreground) 30%, transparent) 55%,
                transparent 78%),
    linear-gradient(to bottom,
                color-mix(in oklab, var(--foreground) 35%, transparent) 0%,
                color-mix(in oklab, var(--foreground) 10%, transparent) 26%,
                color-mix(in oklab, var(--foreground) 40%, transparent) 70%,
                color-mix(in oklab, var(--foreground) 68%, transparent) 100%);
}

/* Nothing is written on the film until it has been running a moment — the
   `is-told` class is added a second in, by Sections.functionText(). */
.function__caption > p {
  position: relative;
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity .8s cubic-bezier(.4, 0, .2, 1),
              transform .8s cubic-bezier(.4, 0, .2, 1);
}

.function__frame.is-told .function__caption::before { opacity: 1; }

.function__frame.is-told .function__caption > p {
  opacity: 1;
  transform: translateY(0);
}

/* The name lands first, the details follow it down the card. */
.function__frame.is-told .function__when  { transition-delay: .2s; }
.function__frame.is-told .function__note  { transition-delay: .4s; }
.function__frame.is-told .function__place { transition-delay: .55s; }

@media (prefers-reduced-motion: reduce) {
  .function__caption::before,
  .function__caption > p { transition-duration: .01ms; }

  .function__caption > p { transform: none; }

  /* Written out in full so these beat the staggered delays above: with motion
     turned down the lines arrive together rather than in sequence. */
  .function__frame.is-told .function__when,
  .function__frame.is-told .function__note,
  .function__frame.is-told .function__place { transition-delay: 0s; }
}

.function__name {
  margin: 0;
  font-family: var(--font-script);
  font-size: 3.25rem;
  line-height: 1.1;
}

.function__when {
  margin: 0;
  font-size: 1.375rem;
  line-height: 1.45;
}

.function__note {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.45;
  color: color-mix(in oklab, var(--background) 88%, transparent);
}

/* The venue is set apart from the note above it: a gold outline with a map
   pin, so it reads as where rather than as one more line about what. No fill,
   so the film shows through. As on shubham-weds-simran. The words sit in
   their own span so a two-line address stays one flex item beside the pin. */
.function__place {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin: .25rem 0 0;
  padding: .3rem .95rem .3rem .75rem;
  border: 1px solid color-mix(in oklab, var(--gold-light) 70%, transparent);
  border-radius: 9999px;
  color: var(--gold-light);
  font-size: .95rem;
  font-weight: 600;
  line-height: 1.5;
  transition: background-color .2s ease;
}

/* Each pill is a link to its venue on Google Maps. The caption lets taps
   through to the film, so the link takes them back, but only once the words
   are showing: an invisible link over the film would catch taps meant for
   the card. Its ::after spreads over the whole pill, pin included. */
.function__place a {
  color: inherit;
  text-decoration: none;
}

.function__place a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 9999px;
}

.function__frame.is-told .function__place a { pointer-events: auto; }

.function__place:hover,
.function__place:has(a:focus-visible) {
  background: color-mix(in oklab, var(--gold-light) 22%, transparent);
}

.function__place:has(a:focus-visible) {
  outline: 2px solid var(--gold-light);
  outline-offset: 2px;
}

.function__place::before {
  content: "";
  flex: none;
  width: 1em;
  height: 1em;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a7 7 0 0 0-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0-7-7zm0 9.5A2.5 2.5 0 1 1 12 6.5a2.5 2.5 0 0 1 0 5z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a7 7 0 0 0-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0-7-7zm0 9.5A2.5 2.5 0 1 1 12 6.5a2.5 2.5 0 0 1 0 5z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.functions__arrow {
  pointer-events: none;
  position: absolute;
  top: 50%;
  z-index: 20;
  display: none;
  align-items: center;
  justify-content: center;
  padding: .75rem;
  border-radius: 9999px;
  background: color-mix(in oklab, var(--background) 90%, transparent);
  color: var(--primary);
  box-shadow: var(--shadow-lg),
              0 0 0 1px color-mix(in oklab, var(--gold) 50%, transparent);
  opacity: 0;
  transition: opacity .15s cubic-bezier(.4, 0, .2, 1);
}

.functions__arrow svg { width: 1.25rem; height: 1.25rem; }

.functions__arrow--left  { left: .5rem;  transform: translateY(-50%); }
.functions__arrow--right { right: .5rem; transform: translateY(-50%) rotate(180deg); }

.functions__arrow.is-active { pointer-events: auto; opacity: 1; }

.functions__hint {
  display: none;
  margin-top: 1.5rem;
  text-align: center;
  font-size: .75rem;
  line-height: 1.3333;
  text-transform: uppercase;
  letter-spacing: .25em;
  color: var(--muted-foreground);
}

@media (min-width: 768px) {
  .functions__viewport { margin-top: 3.5rem; }

  .functions__track {
    flex-direction: row;
    align-items: stretch;
    gap: 2rem;
    padding: 0 3rem 2rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    cursor: grab;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .functions__track::-webkit-scrollbar { display: none; }

  .function { width: 26rem; max-width: none; scroll-snap-align: center; }
  .function__frame { aspect-ratio: 3 / 4; }

  .function__caption { gap: 1rem; padding: 13% 2rem 2rem; }
  .function__name    { font-size: 3.75rem; }
  .function__when    { font-size: 1.5rem; }

  .function:hover .function__frame { transform: scale(1.03); }

  .functions__arrow { display: flex; }
  .functions__hint { display: block; }
}

@media (min-width: 1024px) {
  .functions__track { padding-left: 5rem; padding-right: 5rem; }
}

/* --------------------------------------------------------------------------
   7. Venue
   -------------------------------------------------------------------------- */
.venue { position: relative; overflow: hidden; }

.venue__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .45;
}

.venue__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in oklab, var(--background) 60%, transparent);
}

.venue__inner {
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;
  margin: 0 auto;
  max-width: 42rem;
  padding: 5rem 1.5rem;
  text-align: center;
}

.venue__title {
  font-family: var(--font-script);
  font-size: 2.25rem;
  line-height: 1.1111;
  font-weight: 400;
  color: var(--primary);
}

.venue__map {
  position: relative;
  width: 100%;
  overflow: hidden;
  border: 2px solid color-mix(in oklab, var(--gold) 70%, transparent);
  border-radius: calc(var(--radius) + 12px);
  box-shadow: var(--shadow-2xl);
}

.venue__map iframe { width: 100%; height: 16rem; }

.directions {
  display: inline-flex;
  align-items: center;
  gap: .75rem;
  padding: .75rem 2rem;
  border-radius: 9999px;
  background: var(--primary);
  color: var(--primary-foreground);
  font-size: .875rem;
  text-transform: uppercase;
  letter-spacing: .25em;
  box-shadow: var(--shadow-lg);
  transition: transform .15s cubic-bezier(.4, 0, .2, 1);
}

.directions:hover { transform: scale(1.05); }
.directions svg { width: 1.25rem; height: 1.25rem; }

/* The paper plane that flies across before Google Maps opens. */
.plane {
  pointer-events: none;
  position: fixed;
  inset: 0;
  z-index: 40;
  overflow: hidden;
}

.plane svg {
  position: absolute;
  left: 0;
  top: 50%;
  width: 4rem;
  height: 4rem;
  color: var(--primary);
  filter: drop-shadow(0 4px 4px #00000026);
  animation: plane-fly 2.2s cubic-bezier(.4, 0, .4, 1) forwards;
}

@media (min-width: 768px) {
  .venue__title { font-size: 3rem; line-height: 1; }
  .venue__map iframe { height: 20rem; }
}

/* --------------------------------------------------------------------------
   8. RSVP
   -------------------------------------------------------------------------- */
.rsvp {
  position: relative;
  overflow: hidden;
  background: var(--background);
  padding: 5rem 0 2rem;
}

.rsvp__glow {
  pointer-events: none;
  position: absolute;
  width: 18rem;
  height: 18rem;
  border-radius: 9999px;
  filter: blur(64px);
  animation: soft-float 4s ease-in-out infinite;
}

.rsvp__glow--gold {
  left: -5rem;
  top: 2.5rem;
  background: color-mix(in oklab, var(--gold) 10%, transparent);
}

.rsvp__glow--rose {
  right: -5rem;
  bottom: 2.5rem;
  background: color-mix(in oklab, var(--primary) 10%, transparent);
  animation-delay: 2s;
}

.rsvp__inner {
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;
  margin: 0 auto;
  max-width: 48rem;
  padding: 0 1.5rem;
  text-align: center;
}

/* Each block slides up once the section scrolls into view. */
[data-rsvp-reveal] {
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity .7s cubic-bezier(.4, 0, .2, 1), transform .7s cubic-bezier(.4, 0, .2, 1);
}

.rsvp.is-visible [data-rsvp-reveal] { opacity: 1; transform: translateY(0); }

.rsvp__intro > * + * { margin-top: .75rem; }

.rsvp__lede {
  max-width: 28rem;
  font-size: .875rem;
  line-height: 1.625;
  color: var(--muted-foreground);
}

.rsvp__card {
  position: relative;
  width: 100%;
  overflow: hidden;
  padding: .25rem;
  border: 2px solid color-mix(in oklab, var(--gold) 70%, transparent);
  border-radius: calc(var(--radius) + 12px);
  background: color-mix(in oklab, var(--card) 80%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  animation: rsvp-glow 3s ease-in-out infinite;
}

.rsvp__card[data-rsvp-reveal] { transform: translateY(2rem); }

.rsvp__card-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .75rem 0;
  border-radius: calc(var(--radius) + 8px) calc(var(--radius) + 8px) 0 0;
  background: linear-gradient(to right,
              color-mix(in oklab, var(--gold) 20%, transparent),
              color-mix(in oklab, var(--gold) 30%, transparent),
              color-mix(in oklab, var(--gold) 20%, transparent));
}

.rsvp__card-head svg { width: 1rem; height: 1rem; fill: var(--primary); }

.rsvp__card-head span {
  font-size: .75rem;
  line-height: 1.3333;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .25em;
  color: var(--primary);
}

.rsvp__form {
  position: relative;
  width: 100%;
  height: 28rem;
  overflow: hidden;
  border-radius: 0 0 calc(var(--radius) + 8px) calc(var(--radius) + 8px);
  background: var(--background);
}

.rsvp__form { overflow-y: auto; }

/* The sample RSVP form. Built from the same tokens as the rest of the page,
   so the card reads as one piece. */
.rsvp__fields {
  display: flex;
  flex-direction: column;
  gap: .7rem;
  padding: 1.1rem 1.25rem 1.35rem;
}

/* The yes/no toggle — two buttons carrying aria-pressed, styled as one pill. */
.rsvp__choice {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .4rem;
}

.rsvp__pick {
  padding: .55rem .5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--background);
  color: var(--muted-foreground);
  font-family: var(--font-serif);
  font-size: .8rem;
  cursor: pointer;
  transition: background-color .25s ease, border-color .25s ease, color .25s ease;
}

.rsvp__pick:hover { border-color: var(--gold); }

.rsvp__pick[aria-pressed="true"] {
  border-color: var(--primary);
  background: var(--primary);
  color: var(--primary-foreground);
  box-shadow: var(--shadow-sm);
}

.rsvp__field { display: block; min-width: 0; }

.rsvp__field > span {
  display: block;
  margin-bottom: .3rem;
  font-size: .62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--muted-foreground);
}

.rsvp__field input,
.rsvp__field select,
.rsvp__field textarea {
  width: 100%;
  padding: .55rem .7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-serif);
  font-size: .85rem;
  line-height: 1.4;
  transition: border-color .2s ease, box-shadow .2s ease;
}

.rsvp__field textarea { resize: vertical; }

.rsvp__field input:focus-visible,
.rsvp__field select:focus-visible,
.rsvp__field textarea:focus-visible {
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--gold) 28%, transparent);
}

.rsvp__field ::placeholder { color: color-mix(in oklab, var(--muted-foreground) 70%, transparent); }

.rsvp__send {
  margin-top: .2rem;
  padding: .7rem 1.2rem;
  border: 0;
  border-radius: var(--radius);
  background: var(--primary);
  color: var(--primary-foreground);
  font-family: var(--font-serif);
  font-size: .72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .22em;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .2s ease, box-shadow .2s ease, opacity .2s ease;
}

.rsvp__send:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.rsvp__send:disabled { opacity: .65; cursor: default; transform: none; }

/* Shown in place of the form once a reply is acknowledged. */
.rsvp__thanks {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  height: 100%;
  padding: 2rem 1.5rem;
  text-align: center;
}

.rsvp__thanks svg { width: 2rem; height: 2rem; fill: var(--rose-400); }

.rsvp__thanks h3 {
  margin: 0;
  font-family: var(--font-script);
  font-size: 1.9rem;
  font-weight: 400;
  color: var(--primary);
}

.rsvp__thanks p {
  margin: 0;
  max-width: 18rem;
  font-size: .8rem;
  color: var(--muted-foreground);
}

.rsvp__again {
  margin-top: .4rem;
  padding: .5rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--muted-foreground);
  font-family: var(--font-serif);
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}

.rsvp__again:hover { border-color: var(--gold); color: var(--primary); }

/* "Regretfully no" has no seat to confirm, so the card answers differently:
   a moon instead of the heart, and the guest's own name in the heading. */
.rsvp__thanks--regrets svg { fill: var(--gold); animation: rsvp-moon-glow 5.5s ease-in-out infinite; }

@keyframes rsvp-moon-glow {
  0%, 100% { opacity: .8; transform: translateY(2px); }
  50%      { opacity: 1;  transform: translateY(-3px); }
}

.rsvp__guest { font-style: italic; color: var(--gold); }

.rsvp__note {
  max-width: 24rem;
  font-size: .75rem;
  line-height: 1.625;
  color: var(--muted-foreground);
}

@media (min-width: 768px) {
  .rsvp__form { height: 30rem; }
}

/* --------------------------------------------------------------------------
   Keyframes
   -------------------------------------------------------------------------- */
@keyframes float-heart {
  0%   { opacity: 0; transform: translateY(0) translateX(0) rotate(0); }
  10%  { opacity: .8; }
  90%  { opacity: .6; }
  100% { opacity: 0; transform: translateY(-110vh) translateX(var(--drift)) rotate(360deg); }
}

@keyframes bubble-rise {
  0%   { opacity: 0; transform: translateX(-50%) scale(.3) rotate(0); }
  15%  { opacity: 1;
         transform: translate(calc(-50% + var(--tx) * .1), -8vh) scale(1.15) rotate(calc(var(--rot) * .2)); }
  100% { opacity: 0;
         transform: translate(calc(-50% + var(--tx)), -120vh) scale(.75) rotate(var(--rot)); }
}

@keyframes petal-fall {
  0%   { opacity: 0; transform: translateY(-10vh) rotate(0); }
  10%  { opacity: 1; }
  100% { opacity: .2; transform: translate(var(--drift), 115vh) rotate(var(--rot)); }
}

@keyframes curtain-reveal {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.05); }
}

@keyframes text-reveal {
  0%   { opacity: 0; filter: blur(4px); transform: translateY(20px) scale(.96); }
  100% { opacity: 1; filter: blur(0);   transform: translateY(0) scale(1); }
}

@keyframes soft-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}

@keyframes pulse-glow {
  0%, 100% { box-shadow: 0 0 0 0 var(--gold-light); }
  50%      { box-shadow: 0 0 32px 8px var(--gold-light); }
}

@keyframes rsvp-glow {
  0%, 100% { box-shadow: 0 0 0 0 var(--gold-light), 0 30px 80px -30px #00000059; }
  50%      { box-shadow: 0 0 24px 4px var(--gold-light), 0 30px 80px -30px #00000059; }
}

@keyframes plane-fly {
  0%   { opacity: 0; transform: translate(-20vw, 30vh) rotate(-12deg) scale(.6); }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { opacity: 0; transform: translate(115vw, -40vh) rotate(-12deg) scale(1.2); }
}

@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------------------------
   9. Thank you
   A postcard home — airmail border, the couple's initials for a stamp and a
   postmark from the wedding — on the maroon ground. The same postcard as
   shubham-weds-simran, in English as on nikita-weds-poojan, set in the
   page's own Great Vibes.
   -------------------------------------------------------------------------- */
.thanks {
  padding: 5rem 1.5rem 4rem;
  background: var(--primary);
}

/* The airmail stripes are the postcard's own background, showing as a
   border around the paper laid over it. */
.postcard {
  max-width: 820px;
  margin: 0 auto;
  padding: 10px;
  border-radius: 6px;
  background: repeating-linear-gradient(135deg,
    var(--gold) 0 14px, var(--background) 14px 22px,
    color-mix(in oklab, var(--primary), var(--rose-400)) 22px 36px, var(--background) 36px 44px);
  box-shadow: 0 30px 60px -25px #00000080;
}

.postcard__paper {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 2rem;
  padding: 2rem;
  text-align: left;
  color: var(--foreground);
  background: var(--background);
  border-radius: 2px;
}

.postcard__message {
  padding-right: 2rem;
  border-right: 1px solid color-mix(in oklab, var(--primary) 20%, transparent);
}

.postcard__thanks {
  margin-bottom: .75rem;
  font-family: var(--font-script);
  font-size: clamp(2.8rem, 9vw, 3.6rem);
  line-height: 1.1;
  color: var(--primary);
}

.postcard__text {
  font-size: 1.1rem;
  line-height: 1.65;
}

.postcard__sign {
  margin-top: 1.25rem;
  font-size: 1.05rem;
  font-style: italic;
}

.postcard__sign span {
  display: block;
  font-family: var(--font-script);
  font-size: 2.25rem;
  font-style: normal;
  line-height: 1.3;
  color: var(--primary);
}

.postcard__address { display: flex; flex-direction: column; }

.postcard__marks {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-bottom: 1.75rem;
}

/* Inked over the stamp's corner, as a real one would be. */
.postcard__postmark {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 92px;
  height: 92px;
  margin-right: -12px;
  font-size: .56rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--primary) 70%, transparent);
  border: 2px solid color-mix(in oklab, var(--primary) 55%, transparent);
  border-radius: 50%;
  transform: rotate(-14deg);
}

.postcard__postmark strong {
  margin: .2rem 0;
  font-size: .78rem;
  letter-spacing: .05em;
}

/* The perforated edge is a dotted border in the paper's colour, drawn over
   the stamp's white. */
.postcard__stamp {
  width: 88px;
  height: 104px;
  padding: 5px;
  background: #fff;
  border: 5px dotted var(--background);
  filter: drop-shadow(0 2px 4px #00000026);
}

/* The initials are stacked, N over S, since Great Vibes runs too wide to set
   them side by side on a stamp this size. */
.postcard__monogram {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  overflow: hidden;
  font-family: var(--font-script);
  font-size: 1.6rem;
  line-height: .85;
  color: var(--primary);
  background: var(--card);
  outline: 1px solid color-mix(in oklab, var(--gold) 60%, transparent);
  outline-offset: -4px;
}

.postcard__monogram i {
  margin: .05em 0;
  font-style: normal;
  font-size: .8em;
  color: var(--gold);
}

.postcard__to {
  margin-bottom: .25rem;
  font-size: .7rem;
  letter-spacing: .25em;
  text-transform: uppercase;
  color: var(--gold);
}

.postcard__lines {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The address, handwritten along ruled lines. */
.postcard__lines li {
  padding: .35rem 0 .1rem .75rem;
  font-family: var(--font-script);
  font-size: 2rem;
  line-height: 1.4;
  color: var(--primary);
  border-bottom: 1px solid color-mix(in oklab, var(--primary) 25%, transparent);
}

/* The postcard slides up once the section scrolls into view. */
[data-thanks-reveal] {
  opacity: 0;
  transform: translateY(2rem);
  transition: opacity .8s cubic-bezier(.4, 0, .2, 1), transform .8s cubic-bezier(.4, 0, .2, 1);
}

.thanks.is-visible [data-thanks-reveal] { opacity: 1; transform: translateY(0); }

@media (max-width: 680px) {
  .thanks { padding-top: 4rem; }

  .postcard__paper {
    grid-template-columns: 1fr;
    gap: 1.5rem;
    padding: 1.5rem;
  }

  .postcard__message {
    padding: 0 0 1.5rem;
    border-right: 0;
    border-bottom: 1px solid color-mix(in oklab, var(--primary) 20%, transparent);
  }

  /* On a phone the stamp leads the address side, so it sits at the left. */
  .postcard__marks { justify-content: flex-start; }
}

/* --------------------------------------------------------------------------
   Render cost

   The sections below the invitation are tall, image-backed and full of
   animation. `content-visibility` lets the browser skip laying out and
   painting the ones that are off screen — the work still happens, but when
   the guest scrolls to them rather than all at once on a phone that is also
   decoding video. `contain-intrinsic-size` keeps the scrollbar honest in the
   meantime, so skipping does not make the page jump.
   -------------------------------------------------------------------------- */
.venue,
.rsvp,
.thanks {
  content-visibility: auto;
  contain-intrinsic-size: auto 100vh;
}

/* Three sections are deliberately left out of that.

   The save-the-date is the first thing under the invitation, and its scratch
   canvas is sized on load — it needs a real box from the start.

   The memories strip and the function films are left out because skipping
   their rendering also hides their contents from the observers that decide
   what to fetch: a card with no box never reports as approaching the
   viewport, and the head start those observers exist to give would be lost
   exactly where it matters most. Paint work saved is not worth a video that
   starts downloading later. */

/* --------------------------------------------------------------------------
   Reduced motion

   A guest who has asked their phone for less motion gets a still invitation:
   no drifting particles, no spinning disc, no glows breathing in and out.
   Everything still arrives and still reads; it simply stops moving.

   The particle effects check this too (assets/js/network.js) and build none
   of the elements at all, which saves the work rather than just hiding it.
   This block covers what is written into the stylesheet.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .hearts,
  .petals,
  .burst { display: none; }

  .invite__glow,
  .rsvp__glow { animation: none; }

  .invite__floral { transition: none; }
  .invite__floral img { animation: none; }

  .rsvp__card,
  .music-toggle__ring,
  .music-toggle__disc,
  .invite-btn,
  .invite__scroll-inner,
  .rsvp__thanks--regrets svg { animation: none; }

  /* The reveal still happens — a guest should see the names arrive — but as a
     fade, with no travel and no blur. */
  .reveal {
    animation-name: text-fade;
    animation-duration: .2s;
  }

  .intro { transition-duration: .2s; }

  .memories__track,
  .functions__track { scroll-behavior: auto; }


  /* The hand still shows — it is the only thing telling a guest the card
     is theirs to scratch — but it stops travelling and simply sits where
     the gesture begins. */
  .scratch__hand { animation: none; opacity: 1; transform: none; }

  .memories__item { transition: none; }

  [data-thanks-reveal] { transition: none; transform: none; }
}

@keyframes text-fade {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

.design-credit {
  padding: .6rem 1rem calc(.6rem + env(safe-area-inset-bottom));
  border-top: 1px solid color-mix(in oklab, var(--gold) 24%, transparent);
  text-align: center;
  background: var(--background);
}
.design-credit p { margin: 0; font-size: .625rem; letter-spacing: .06em; color: var(--muted-foreground); }
.design-credit a { color: var(--primary); font-weight: 600; text-decoration: none; }
.design-credit a:hover { color: var(--gold); }
