/* Gulo & Giorgi - wedding invitation.
   Single stylesheet for index.html. Mobile-first; from 769px up the whole site
   is replaced by #desktop-notice, so every rule below that is the real design. */

/* ===== FONTS ===== */
/* font-display: block is only safe because #loading-screen covers the block
   period. Never ship one without the other. */
@font-face {
  font-family: 'DM Medea';
  src: url('bpg_nino.otf') format('opentype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'DM Medea Display';
  src: url('DM-Medea.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

/* ===== RESET ===== */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* ===== VARIABLES =====
   Pulled from the artwork: ivory lace of the doors, the hazy sea behind the
   arch, champagne of the lace bow. --display is the deep end of that sea and
   carries every headline. */
:root {
  --vh: 1vh;

  --paper:     #f7f3ea;
  --paper-2:   #eef1ed;
  --card:      #fdfbf6;
  --ink:       #48453d;
  --ink-soft:  #8a857a;
  --sea:       #7f9fb3;
  --sea-soft:  #bccdd8;
  --display:   #4e6b7d;
  --gold:      #c2a679;
  --rule:      #d9d2c3;
  --border:    #e3ddd0;

  --ease-out:  cubic-bezier(0.22, 1, 0.36, 1);
  --ease-io:   cubic-bezier(0.65, 0, 0.35, 1);
}

/* Georgian and English are set from different families at different optical
   sizes, so each language gets its own display numbers. */
:root {
  --fs-names-sm:  32px;
  --fs-names-lg:  40px;
  --fs-title-sm:  23px;
  --fs-title-lg:  32px;
  --fs-thanks:    18px;
  --fs-tt-title:  21px;
  --fs-thankyou:  19px;
}

body.lang-en {
  --fs-names-sm:  38px;
  --fs-names-lg:  48px;
  --fs-title-sm:  30px;
  --fs-title-lg:  36px;
  --fs-thanks:    25px;
  --fs-tt-title:  25px;
  --fs-thankyou:  22px;
}

/* ===== BASE =====
   overflow-x and overscroll-behavior belong to <html> alone. On <body> too,
   overflow-x turns body into a scroller that can never scroll, a macOS trackpad
   latches onto it, and overscroll-behavior stops the gesture reaching <html>. */
html {
  scroll-behavior: smooth;
  overflow-x: hidden;
  overscroll-behavior-y: none;
}

body {
  font-family: 'DM Medea', 'Noto Serif Georgian', serif;
  background-color: var(--paper);
  color: var(--ink);
  max-width: 100%;
  -webkit-font-smoothing: antialiased;
}

body.lang-en {
  font-family: 'Cormorant Garamond', serif;
}

input, textarea, select, button {
  font-family: inherit;
}

/* Intro / loader scroll lock. Never position:fixed or overflow:hidden: Chrome
   iOS opened from a chat app freezes its viewport short the moment the document
   stops being scrollable. script.js traps touchmove and scroll instead. */
html.is-locked,
html.is-locked body {
  overscroll-behavior: none;
  touch-action: none;
}

/* ===== CUSTOM SCROLLBAR =====
   Thumb starts transparent and fades in with .scrollbar-ready so it does not
   draw a stripe down the side of the entry gate. */
::-webkit-scrollbar {
  width: 5px;
}
::-webkit-scrollbar-track {
  background-color: var(--paper-2);
}
::-webkit-scrollbar-thumb {
  background-color: transparent;
  border-radius: 3px;
  transition: background-color 0.6s ease;
}
body.scrollbar-ready::-webkit-scrollbar-thumb {
  background-color: var(--sea-soft);
}
html {
  scrollbar-color: transparent var(--paper-2);
  scrollbar-width: thin;
  transition: scrollbar-color 0.6s ease;
}
html.scrollbar-ready {
  scrollbar-color: var(--sea-soft) var(--paper-2);
}

/* ===== LOADING SCREEN ===== */
#loading-screen {
  position: fixed;
  inset: 0;
  z-index: 20000;
  background-color: var(--paper);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  transition: opacity 0.7s ease;
}

#loading-screen.fade-out {
  opacity: 0;
  pointer-events: none;
}

#loading-screen.gone {
  display: none;
}

.loader-ring {
  width: 54px;
  height: 54px;
  border: 2px solid rgba(127, 159, 179, 0.24);
  border-top-color: var(--sea);
  border-radius: 50%;
  animation: loaderSpin 1s linear infinite;
}

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

/* Scaled rather than animated on width so it stays on the compositor. */
.loader-track {
  width: 132px;
  height: 2px;
  background-color: rgba(127, 159, 179, 0.2);
  border-radius: 1px;
  overflow: hidden;
}

.loader-bar {
  width: 100%;
  height: 100%;
  background-color: var(--sea);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.35s ease;
}

/* System stack on purpose: every webfont here is font-display: block, so one
   would render nothing during exactly the seconds this number matters. */
.loader-pct {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 12px;
  letter-spacing: 0.1em;
  padding-left: 0.1em;
  color: var(--sea);
  opacity: 0.8;
}

/* ===== ENTRY GATE ===== */
#intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background-color: var(--paper);
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: opacity 1.1s ease;
}

#intro-overlay.fade-out {
  opacity: 0;
  pointer-events: none;
}

#intro-overlay.gone {
  display: none;
}

#intro-poster,
#intro-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  pointer-events: none;
}

#intro-video { z-index: 1; }

#intro-poster {
  z-index: 2;
  transition: opacity 0.35s ease;
}

#intro-poster.hidden {
  opacity: 0;
}

/* iOS blocks autoplay outright in Low Power Mode and the play() rejection is
   the only signal. This cover takes over as the tap target. */
.intro-tap {
  position: absolute;
  inset: 0;
  z-index: 4;
  background: url('1.jpg') center / cover no-repeat;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
  cursor: pointer;
}

.intro-tap.is-visible {
  opacity: 1;
  pointer-events: auto;
}

#intro-click {
  position: absolute;
  top: 80%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  user-select: none;
  -webkit-user-select: none;
  white-space: nowrap;
  opacity: 0;
  animation: introFadeIn 1.4s ease 0.4s forwards;
  transition: opacity 0.5s ease;
  /* The doors are ivory on ivory, so the label brings its own ground. */
  padding: 15px 26px;
  border-radius: 20px;
  background-color: rgba(247, 243, 234, 0.66);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  box-shadow: 0 6px 22px rgba(78, 107, 125, 0.12);
}

/* Held until the loader clears, so the guest never uncovers an animation that
   already played behind the spinner. */
html.is-loading #intro-click {
  animation-play-state: paused;
}

.intro-en {
  font-family: 'Cormorant Garamond', serif;
  font-size: 12px;
  letter-spacing: 0.4em;
  padding-left: 0.4em;
  color: var(--ink);
  font-weight: 500;
}

.intro-divider {
  width: 34px;
  height: 1px;
  background-color: var(--gold);
}

.intro-ka {
  font-size: 19px;
  color: var(--display);
  letter-spacing: 0.04em;
}

/* ===== LANGUAGE TOGGLE ===== */
/* ===== TOGGLES =====
   Language top-left, music top-right, both appear once the gate has opened. */
#lang-toggle,
#music-toggle {
  position: fixed;
  top: 1rem;
  z-index: 500;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}

#lang-toggle  { left: 1rem; }
#music-toggle { right: 1rem; }

#music-toggle.visible,
#lang-toggle.visible {
  opacity: 1;
  pointer-events: auto;
}

.lang-switch {
  position: relative;
  width: 72px;
  height: 34px;
  background-color: rgba(247, 243, 234, 0.82);
  border-radius: 17px;
  border: 1px solid rgba(217, 210, 195, 0.85);
  display: flex;
  align-items: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  -webkit-user-select: none;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.lang-switch__thumb {
  position: absolute;
  left: 3px;
  width: 31px;
  height: 26px;
  background-color: rgba(127, 159, 179, 0.16);
  border: 1px solid rgba(127, 159, 179, 0.45);
  border-radius: 13px;
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.lang-switch.lang-en .lang-switch__thumb {
  transform: translateX(35px);
}

.lang-switch__label {
  flex: 1;
  text-align: center;
  font-size: 18px;
  line-height: 1;
  position: relative;
  z-index: 1;
  opacity: 0.4;
  transition: opacity 0.28s ease;
}

.music-switch {
  position: relative;
  width: 52px;
  height: 29px;
  background-color: rgba(138, 133, 122, 0.42);
  border-radius: 15px;
  border: 1px solid rgba(217, 210, 195, 0.85);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  -webkit-user-select: none;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background-color 0.28s ease;
}

.music-switch.playing {
  background-color: rgba(78, 107, 125, 0.75);
}

.music-switch__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 23px;
  height: 23px;
  background-color: #ffffff;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  line-height: 1;
  color: var(--display);
  box-shadow: 0 1px 3px rgba(72, 69, 61, 0.3);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.music-switch.playing .music-switch__thumb {
  transform: translateX(23px);
}

.lang-switch__label--ka { opacity: 1; }
.lang-switch.lang-en .lang-switch__label--ka { opacity: 0.4; }
.lang-switch.lang-en .lang-switch__label--en { opacity: 1; }

/* ===== SITE ===== */
#site {
  position: relative;
}

/* ===== HERO ===== */
.hero {
  position: relative;
  z-index: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow: hidden;
  background-color: var(--paper);
}

/* The pin. Must stay after the vh/svh floors above. */
.vh-lock .hero {
  min-height: calc(var(--vh) * 100);
}

.hero-media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* Starts at the intro clip's exact framing (cover, centred, no scale) so the
   hand-over from the last video frame is invisible; the drift begins after. */
.hero-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transform-origin: 50% 40%;
}

.hero.revealed .hero-still {
  animation: heroDrift 28s ease-in-out 1.2s infinite alternate;
}

@keyframes heroDrift {
  from { transform: scale(1) translate3d(0, 0, 0); }
  to   { transform: scale(1.08) translate3d(0, -1.2%, 0); }
}

/* Mutes the bright sky behind the names and melts the hem of the dress into
   the first paper sheet. The sky is already pale, so the wash stays light and
   readability comes from the halo on the type. */
.hero-veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background-image:
    radial-gradient(ellipse 90% 30% at 50% 16%,
      rgba(247, 243, 234, 0.62) 0%,
      rgba(247, 243, 234, 0.3) 55%,
      rgba(247, 243, 234, 0) 80%),
    linear-gradient(to top,
      var(--paper) 0%,
      rgba(247, 243, 234, 0) 18%);
}

/* A slow band of sunlight sliding over the sea and the arch. */
.hero-shimmer {
  position: absolute;
  inset: -20% -60%;
  z-index: 2;
  pointer-events: none;
  background-image: linear-gradient(105deg,
    rgba(255, 252, 240, 0) 40%,
    rgba(255, 252, 240, 0.28) 50%,
    rgba(255, 252, 240, 0) 60%);
  transform: translate3d(-60%, 0, 0);
  opacity: 0;
}

.hero.revealed .hero-shimmer {
  animation: heroShimmer 9s ease-in-out 2.4s infinite;
}

@keyframes heroShimmer {
  0%   { transform: translate3d(-60%, 0, 0); opacity: 0; }
  15%  { opacity: 1; }
  55%  { opacity: 1; }
  70%  { transform: translate3d(60%, 0, 0); opacity: 0; }
  100% { transform: translate3d(60%, 0, 0); opacity: 0; }
}

.hero-center {
  position: relative;
  z-index: 3;
  text-align: center;
  padding: 9vh 22px 0;
  max-width: 560px;
}

.vh-lock .hero-center {
  padding-top: calc(var(--vh) * 9);
}

.h-eyebrow,
.h-rule,
.h-date {
  opacity: 0;
}

.hero.revealed .h-eyebrow { animation: fadeInUp 1s var(--ease-out) 0.1s forwards; }
.hero.revealed .h-rule    { animation: ruleGrow 1.1s var(--ease-out) 1.25s forwards; }
.hero.revealed .h-date    { animation: fadeInUp 1s var(--ease-out) 1.45s forwards; }
.hero.revealed .h-scroll  { animation: fadeInUpCentered 0.9s ease 2s forwards; }

/* Wide, soft white halo: a hard shadow on a pale painting reads as dirt. */
.h-eyebrow,
.h-names,
.h-date {
  text-shadow:
    0 0 8px rgba(253, 251, 246, 0.95),
    0 0 22px rgba(253, 251, 246, 0.85),
    0 1px 3px rgba(253, 251, 246, 0.9);
}

.h-eyebrow {
  font-family: 'Cormorant Garamond', serif;
  font-size: 14px;
  letter-spacing: 0.36em;
  padding-left: 0.36em;
  color: var(--display);
  margin-bottom: 16px;
}

.h-names {
  font-family: 'DM Medea Display', serif;
  font-size: var(--fs-names-sm);
  font-weight: normal;
  color: var(--display);
  letter-spacing: 0.03em;
  /* The display face's Georgian descenders hang well below the line box. */
  line-height: 1.55;
  white-space: nowrap;
}

body.lang-en .h-names {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
}

/* Each glyph of the names is its own span (script.js splitText). They surface
   out of a soft blur one after another, like ink settling on paper.

   The blur is faked with a text-shadow glow over transparent glyphs, never
   with `filter`. WebKit (every iOS browser) clips a filtered element to its
   own box, and DM Medea's swashes reach far outside each letter's box, so a
   filter crops every letter while it animates. Shadows are never clipped.
   --glyph-rgb is the glyph's resting colour as RGB, so the glow matches it. */
.ch {
  display: inline-block;
  --glyph-rgb: 78, 107, 125;
  opacity: 0;
  transform: translate3d(0, 0.35em, 0);
}

.ch.amp {
  --glyph-rgb: 194, 166, 121;
}

.hero.revealed .ch {
  opacity: 1;
  transform: none;
  animation: inkIn 1.1s var(--ease-out) backwards;
  animation-delay: calc(0.35s + var(--i) * 0.07s);
}

.ch--space {
  width: 0.28em;
}

.amp {
  color: var(--gold);
  font-family: 'Cormorant Garamond', serif;
  padding: 0 0.12em;
}

.h-rule {
  width: 46px;
  height: 2px;
  background-color: var(--gold);
  margin: 30px auto 18px;
  box-shadow: 0 0 10px 2px rgba(253, 251, 246, 0.9);
  transform-origin: center;
}

.h-date {
  font-family: 'DM Medea Display', 'Cormorant Garamond', serif;
  font-size: 16px;
  letter-spacing: 0.3em;
  color: var(--ink);
  /* The date sits where the sky meets the arch flowers, the busiest strip of
     the painting, so it gets a soft white wash of its own. Inline-block keeps
     the wash hugging the text instead of spanning the full width. Extra left
     padding offsets the trailing letter-space. */
  display: inline-block;
  padding: 8px 22px 8px calc(22px + 0.3em);
  border-radius: 40px;
  background-image: radial-gradient(ellipse at center,
    rgba(253, 251, 246, 0.82) 0%,
    rgba(253, 251, 246, 0.6) 45%,
    rgba(253, 251, 246, 0) 72%);
}

.h-scroll {
  position: absolute;
  bottom: 5vh;
  left: 50%;
  z-index: 3;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: 16px;
  background-color: rgba(253, 251, 246, 0.82);
  border: 1px solid rgba(217, 210, 195, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 2px 12px rgba(78, 107, 125, 0.1);
  opacity: 0;
}

.vh-lock .h-scroll {
  bottom: calc(var(--vh) * 5);
}

.scroll-label {
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--ink);
}

.scroll-chevron {
  width: 7px;
  height: 7px;
  border-right: 1px solid var(--gold);
  border-bottom: 1px solid var(--gold);
  animation: chevronBounce 3.2s ease-in-out infinite;
}

/* ===== SHEETS =====
   Each section below the hero is a sheet of paper laid over the one before:
   rounded top corners, a shadow cast upward and a negative margin. z-index
   climbs down the page or a later sheet would slide under the one above. */
.sheet {
  position: relative;
  margin-top: -26px;
  padding: 64px 22px 60px;
  border-top-left-radius: 26px;
  border-top-right-radius: 26px;
  background-color: var(--paper);
  box-shadow: 0 -14px 34px rgba(78, 107, 125, 0.1);
  overflow: hidden;
}

.thanks      { z-index: 1; padding-top: 58px; }
.timetable   { z-index: 2; background-color: var(--paper-2); }
.dress       { z-index: 3; }
.reply       { z-index: 4; background-color: var(--paper-2); }
.letterbox   { z-index: 5; }
.site-footer { z-index: 6; background-color: var(--paper-2); padding-bottom: 34px; }

.sheet__body {
  position: relative;
  z-index: 2;
  max-width: 600px;
  margin: 0 auto;
  text-align: center;
}

/* ===== BACKGROUND BLOOMS =====
   Washed-out sprays feathered at the edges, so whatever the sheet clips has
   already faded to nothing. */
.bloom {
  position: absolute;
  z-index: 1;
  width: 128%;
  height: auto;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  opacity: 0.22;
  transform: rotate(var(--rot, 0deg));
  -webkit-mask-image: radial-gradient(ellipse at center, #000 26%, rgba(0, 0, 0, 0.4) 58%, transparent 84%);
  mask-image: radial-gradient(ellipse at center, #000 26%, rgba(0, 0, 0, 0.4) 58%, transparent 84%);
}

.bloom--th-left  { --rot: -8deg;  top: -10%;    left: -50%; }
.bloom--th-right { --rot: 172deg; bottom: -10%; right: -52%; }
.bloom--tt       { --rot: 184deg; bottom: -6%;  left: -46%; opacity: 0.16; }
.bloom--rsvp     { --rot: -6deg;  top: -8%;     left: -44%; opacity: 0.16; }
.bloom--reply    { --rot: 176deg; bottom: -10%; right: -46%; opacity: 0.16; }
.bloom--dress    { --rot: 8deg;   top: -10%;    right: -48%; opacity: 0.16; }

/* ===== SHARED SECTION TYPE ===== */
.section-eyebrow {
  font-family: 'Cormorant Garamond', 'Noto Serif Georgian', serif;
  font-size: 11px;
  letter-spacing: 0.32em;
  color: var(--sea);
  margin-bottom: 22px;
}

.section-title {
  font-family: 'DM Medea Display', serif;
  font-size: var(--fs-title-sm);
  font-weight: normal;
  color: var(--display);
  letter-spacing: 0.02em;
  line-height: 1.3;
}

body.lang-en .section-title {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
}

.section-rule {
  width: 34px;
  height: 1px;
  background-color: var(--gold);
  opacity: 0.6;
  margin: 28px auto 40px;
}

/* ===== SCROLL REVEAL ===== */
.reveal {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition: opacity 0.9s ease, transform 0.9s var(--ease-out);
}

.reveal.visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.reveal--late {
  transition-delay: 0.9s;
}

/* ===== THANKS ===== */
.monogram {
  display: block;
  width: 96px;
  height: auto;
  margin: 0 auto 30px;
  filter: drop-shadow(0 6px 14px rgba(140, 120, 90, 0.18));
}

.reveal.visible .monogram {
  animation: monoFloat 7s ease-in-out 1s infinite;
}

/* The words are the reveal here, so the paragraph itself must not also slide. */
.thanks-text.reveal {
  transform: none;
  opacity: 1;
}

.thanks-text {
  font-family: 'DM Medea', 'Noto Serif Georgian', serif;
  font-size: var(--fs-thanks);
  line-height: 1.85;
  color: var(--display);
  letter-spacing: 0.01em;
  max-width: 330px;
  margin: 0 auto;
}

body.lang-en .thanks-text {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  line-height: 1.5;
}

/* Words ink in one after another once the paragraph scrolls into view.
   Fade and rise only: no filter (WebKit clipping, see .ch), and no glow,
   which on text this small and thin smears into grey blocks. */
.w {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 0.3em, 0);
}

.thanks-text.visible .w {
  opacity: 1;
  transform: none;
  animation: wordIn 1s var(--ease-out) backwards;
  animation-delay: calc(var(--i) * 0.09s);
}

.thanks-sign {
  font-size: 15px;
  color: var(--ink-soft);
  letter-spacing: 0.05em;
  margin-top: 30px;
}

.thanks-names {
  font-family: 'DM Medea Display', serif;
  font-size: 22px;
  color: var(--display);
  margin-top: 6px;
}

body.lang-en .thanks-names {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: 26px;
}

/* ===== COUNTDOWN ===== */
.countdown {
  margin-top: 46px;
}

.ornament {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 26px;
}

.ornament span {
  width: 46px;
  height: 1px;
  background-color: var(--gold);
  opacity: 0.55;
}

.ornament i {
  width: 7px;
  height: 7px;
  border: 1px solid var(--gold);
  transform: rotate(45deg);
}

.countdown-sub {
  font-family: 'DM Medea Display', 'Cormorant Garamond', serif;
  font-size: 15px;
  letter-spacing: 0.08em;
  color: var(--sea);
  margin-bottom: 26px;
}

body.lang-en .countdown-sub {
  font-family: 'Cormorant Garamond', serif;
  font-size: 13px;
  letter-spacing: 0.3em;
}

.countdown-grid {
  display: flex;
  justify-content: center;
  align-items: flex-start;
}

.cd-unit {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 14px;
  min-width: 66px;
}

.cd-unit + .cd-unit::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6%;
  height: 50%;
  width: 1px;
  background-color: var(--rule);
}

/* A fixed window the digits roll through: the outgoing value lifts out of the
   top while the new one rises in from below (script.js, rollTo). */
.cd-num {
  position: relative;
  display: block;
  height: 1.1em;
  width: 2.1ch;
  overflow: hidden;
  font-family: 'Cormorant Garamond', serif;
  font-size: 32px;
  line-height: 1.1;
  color: var(--display);
  font-variant-numeric: tabular-nums;
}

.cd-digit {
  position: absolute;
  inset: 0;
  text-align: center;
}

.cd-digit.is-in {
  animation: digitIn 0.55s var(--ease-out) both;
}

.cd-digit.is-out {
  animation: digitOut 0.55s var(--ease-out) both;
}

.cd-label {
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  margin-top: 10px;
}

body.lang-en .cd-label {
  letter-spacing: 0.2em;
}

/* ===== TIMETABLE ===== */
.tt-list {
  display: flex;
  flex-direction: column;
}

.tt-stop {
  position: relative;
}

.tt-stop + .tt-stop {
  margin-top: 58px;
}

/* The thread from the previous stop. It draws downward only once this stop is
   revealed, so scrolling through the day reads as following a path. */
.tt-stop + .tt-stop::before {
  content: '';
  position: absolute;
  left: 50%;
  top: -58px;
  height: 52px;
  width: 0;
  border-left: 1px dashed var(--gold);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 0.9s var(--ease-io) 0.1s;
}

.tt-stop + .tt-stop.visible::before {
  transform: scaleY(1);
}

.tt-dot {
  position: absolute;
  top: -7px;
  left: 50%;
  z-index: 2;
  width: 13px;
  height: 13px;
  margin-left: -6.5px;
  border-radius: 50%;
  background-color: var(--card);
  border: 1px solid var(--gold);
  box-shadow: 0 0 0 4px var(--paper-2);
  transform: scale(0);
}

.tt-dot::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background-color: var(--gold);
}

.tt-stop.visible .tt-dot {
  animation: dotPop 0.7s var(--ease-out) 0.85s forwards;
}

.tt-card {
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: 22px;
  overflow: hidden;
  box-shadow: 0 10px 26px rgba(78, 107, 125, 0.09);
}

.tt-card--plain {
  padding: 30px 24px 28px;
}

.tt-card__img {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-color: var(--paper-2);
}

/* The painting develops in place, the way watercolour soaks into paper: it
   starts as a pale, soft, washed-out bloom and slowly comes into focus and
   colour. Nothing slides or wipes; only blur, tone and a faint settle. */
.tt-card__img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0;
  filter: blur(14px) saturate(0.35) brightness(1.18);
  transform: scale(1.05);
  transition:
    opacity 1.2s ease 0.2s,
    filter 2.2s var(--ease-out) 0.2s,
    transform 2.8s var(--ease-out) 0.2s;
}

.tt-stop.visible .tt-card__img img {
  opacity: 1;
  filter: blur(0) saturate(1) brightness(1);
  transform: scale(1);
}

/* Fades the illustration into the card so there is no hard image edge. */
.tt-card__img::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 24%;
  background-image: linear-gradient(to bottom, rgba(253, 251, 246, 0), var(--card));
}

.tt-card__body {
  padding: 2px 24px 30px;
}

.tt-card__time {
  font-family: 'Cormorant Garamond', serif;
  font-size: 30px;
  letter-spacing: 0.1em;
  color: var(--gold);
  line-height: 1;
  margin-bottom: 12px;
}

.tt-card__title {
  font-family: 'DM Medea Display', serif;
  font-size: var(--fs-tt-title);
  font-weight: normal;
  color: var(--display);
  letter-spacing: 0.02em;
  line-height: 1.5;
}

body.lang-en .tt-card__title {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
}

.tt-card__rule {
  width: 28px;
  height: 1px;
  background-color: var(--rule);
  margin: 14px auto;
}

.tt-card__venue {
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink);
  letter-spacing: 0.02em;
}

body.lang-en .tt-card__venue {
  font-family: 'Cormorant Garamond', serif;
  font-size: 18px;
}

.tt-card__note {
  font-family: 'Noto Serif Georgian', serif;
  font-size: 13px;
  font-weight: 300;
  line-height: 1.7;
  color: var(--ink-soft);
  margin-top: 4px;
}

body.lang-en .tt-card__note {
  font-family: 'Cormorant Garamond', serif;
  font-size: 15px;
}

/* ===== DRESS CODE ===== */
/* The illustration sits in a soft paper card. multiply drops the painting's
   own ivory into the card colour, so there is no visible rectangle of a
   slightly different white. */
.dress-art {
  position: relative;
  margin: 0 auto;
  max-width: 420px;
  padding: 14px;
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: 20px;
  box-shadow: 0 12px 28px rgba(78, 107, 125, 0.09);
  overflow: hidden;
}

/* Hairline inner frame, like the RSVP card. */
.dress-art::after {
  content: '';
  position: absolute;
  inset: 7px;
  border: 1px solid rgba(194, 166, 121, 0.35);
  border-radius: 15px;
  pointer-events: none;
}

/* Same watercolour "develop" as the timetable paintings: soft, pale and
   blurred, coming into focus in place. Nothing slides. */
.dress-art img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1100 / 614;
  mix-blend-mode: multiply;
  opacity: 0;
  filter: blur(14px) saturate(0.35) brightness(1.18);
  transform: scale(1.05);
  transition:
    opacity 1.2s ease 0.25s,
    filter 2.2s var(--ease-out) 0.25s,
    transform 2.8s var(--ease-out) 0.25s;
}

.dress-art.visible img {
  opacity: 1;
  filter: blur(0) saturate(1) brightness(1);
  transform: scale(1);
}

.dress-orn {
  margin: 30px 0 22px;
}

.dress-text {
  font-family: 'DM Medea', 'Noto Serif Georgian', serif;
  font-size: 16px;
  line-height: 1.95;
  color: var(--ink);
  max-width: 340px;
  margin: 0 auto;
}

body.lang-en .dress-text {
  font-family: 'Cormorant Garamond', serif;
  font-size: 19px;
  line-height: 1.6;
}

/* ===== ENVELOPE =====
   Geometry is in percent of the 740 x 1094 source canvas the layers were cut
   from: the flap hinge sits at 34.64% of its height. */
.env-stage {
  position: relative;
  width: 100%;
  max-width: 340px;
  /* The top ~51% of the envelope canvas is empty air reserved for the open
     flap. Closed, the stage is pulled up into it so the envelope sits close
     under the title; .is-opened hands the room back as the flap lifts. */
  margin: -40% auto 0;
  /* padding-top grows by exactly as much as the letter stands proud of the
     envelope, so the opened card never overlaps the title (script.js). */
  transition: padding-top 2.2s var(--ease-io), margin-top 1.1s var(--ease-io);
}

.env-stage.is-opened {
  margin-top: -6px;
}

/* Tall Georgian ascenders and descenders in the display face need the room. */
.letterbox .section-title {
  line-height: 2;
}

.letterbox .section-rule {
  margin: 12px auto 0;
}

/* Written on the envelope itself, just under its top edge, like a note in
   pen on the paper. Fades out as the flap starts to lift. */
.env-hint {
  position: absolute;
  top: calc(34.64% + 12px);
  left: 0;
  right: 0;
  z-index: 10;
  font-family: 'DM Medea Display', serif;
  font-size: 15px;
  color: var(--display);
  letter-spacing: 0.05em;
  text-shadow: 0 0 6px rgba(253, 251, 246, 0.9), 0 0 14px rgba(253, 251, 246, 0.6);
  pointer-events: none;
  animation: hintBreath 2.6s ease-in-out infinite;
  transition: opacity 0.4s ease;
}

/* The breathing animation owns opacity, so it has to stop for the fade. */
.env-hint.is-hidden {
  animation: none;
  opacity: 0;
}

body.lang-en .env-hint {
  font-family: 'Cormorant Garamond', serif;
  font-size: 17px;
  font-weight: 400;
}

.envelope {
  position: relative;
  width: 100%;
  aspect-ratio: 740 / 1094;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  outline: none;
  animation: envBob 3.8s ease-in-out infinite;
}

.envelope:focus-visible {
  outline: 1px dashed var(--sea);
  outline-offset: 6px;
}

.envelope.is-opening {
  animation: none;
  cursor: default;
}

.env-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* The envelope's shadow lives on its image layers, not on .envelope: a filter
   there would clip the RSVP letter (and its text) on iOS once it grows up
   past the envelope's box. Images have nothing outside their box to lose.
   The pocket's own shadow falls on the letter, which reads as depth. */
.env-back,
.env-pocket {
  filter: drop-shadow(0 12px 14px rgba(78, 107, 125, 0.14));
}

.env-back   { z-index: 1; }
.env-pocket { z-index: 4; }
/* The bow never moves: it ties the envelope and stays put through the opening. */
.env-bow    { z-index: 6; }

/* Closed is rotateX(-180deg): the negative direction makes the flap swing
   toward the guest on its way up, the way a real one lifts. */
.env-flap {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 34.64%;
  z-index: 5;
  transform-origin: 50% 100%;
  transform: perspective(1100px) rotateX(-180deg);
  transform-style: preserve-3d;
  transition: transform 1.25s var(--ease-io);
  pointer-events: none;
}

.env-flap.is-open {
  transform: perspective(1100px) rotateX(0deg);
}

/* Set by script.js the moment the flap passes edge-on. */
.env-flap.is-behind {
  z-index: 2;
}

.env-flap__face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

/* rotateY, not rotateX: the parent's rotateX about the hinge already turns the
   image upside down, and a second X flip would park the tip on the hinge. */
.env-flap__out {
  transform: rotateY(180deg);
}

/* Faces are also swapped explicitly at the edge-on moment, because
   backface-visibility on its own is unreliable in WebKit. */
.env-flap__in                 { opacity: 0; }
.env-flap.is-behind .env-flap__in  { opacity: 1; }
.env-flap.is-behind .env-flap__out { opacity: 0; }

/* The letter, which is also the RSVP card. Its bottom edge is pinned at the
   same spot deep inside the pocket (79.9% of the canvas) from start to finish;
   opening only grows its height upward, so it always comes up OUT of the
   envelope and never floats in front of it. Closed, the closed flap and the
   pocket cover it completely. */
.env-letter {
  position: absolute;
  left: 9.7%;
  width: 80.6%;
  bottom: 20.1%;
  height: 43.4%;
  z-index: 3;
  overflow: hidden;
  /* paper.jpg is a Nano Banana 2 scan of handmade cotton paper. cover, not a
     tile: the letter's height changes and a seam would show. The inset shadow
     darkens the edges slightly, the way real paper catches less light there. */
  background-color: var(--card);
  background-size: cover;
  background-position: center top;
  border-radius: 4px;
  box-shadow:
    0 -4px 16px rgba(78, 107, 125, 0.12),
    inset 0 0 26px rgba(160, 135, 95, 0.14);
  text-align: center;
  pointer-events: none;
  transition: height 2.2s var(--ease-io);
}

.env-letter.is-open {
  pointer-events: auto;
}

/* Hairline frame inset from the paper edge, like a printed card. */
.env-letter::before {
  content: '';
  position: absolute;
  inset: 8px;
  border: 1px solid rgba(194, 166, 121, 0.45);
  border-radius: 2px;
  pointer-events: none;
}

/* padding-bottom keeps the form clear of the pocket: the part of the letter
   below the pocket rim (about 82% of the letter's width) is never visible. */
.env-letter__inner {
  position: relative;
  padding: 28px 18px 82%;
}

.env-letter__title {
  font-family: 'Noto Serif Georgian','DM Medea Display', serif;
  /* Two lines: "ახალდაქორწინებულებს" alone nearly fills the letter's width. */
  font-size: 16px;
  font-weight: normal;
  color: var(--display);
  line-height: 1.4;
}

body.lang-en .env-letter__title {
  font-family: 'Cormorant Garamond', serif;
  font-size: 26px;
  font-weight: 300;
}

/* Petals thrown out of the envelope as it opens. */
.burst-petal {
  position: absolute;
  z-index: 12;
  width: 26px;
  height: auto;
  pointer-events: none;
}

.section-rule--tight {
  margin: 16px auto 22px;
}

/* ===== RSVP FORM (on the letter) =====
   Styled as pen on paper rather than UI: fields are writing lines, choices are
   hand-drawn circles that get inked in, the wishes box is ruled notepaper and
   the button is a letterpress stamp. Guest entries are written in --display,
   the same blue as the headings, like fountain-pen ink. */
.rsvp-form {
  display: flex;
  flex-direction: column;
  gap: 22px;
  text-align: center;
}

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

.rsvp-input,
.rsvp-textarea {
  width: 100%;
  background-color: transparent;
  border: none;
  outline: none;
  border-radius: 0;
  /* 16px minimum, or iOS Safari zooms the whole page on focus */
  font-size: 16px;
  font-family: 'DM Medea', 'Noto Serif Georgian', serif;
  color: var(--display);
  letter-spacing: 0.02em;
  -webkit-appearance: none;
  appearance: none;
}

/* A ruled line to write on, darkening to ink when the guest starts writing. */
.rsvp-input {
  padding: 8px 2px 6px;
  text-align: center;
  border-bottom: 1px solid rgba(72, 69, 61, 0.28);
  transition: border-color 0.3s ease;
}

.rsvp-input:focus {
  border-bottom-color: rgba(78, 107, 125, 0.75);
}

.rsvp-input::placeholder,
.rsvp-textarea::placeholder {
  color: rgba(72, 69, 61, 0.42);
  font-size: 14px;
  letter-spacing: 0.04em;
}

body.lang-en .rsvp-input,
body.lang-en .rsvp-textarea {
  font-family: 'Cormorant Garamond', serif;
  font-size: 18px;
}

/* The two answers stack like options printed on a reply card. */
.rsvp-radios {
  display: grid;
  justify-content: center;
  justify-items: start;
  gap: 12px;
}

.radio-card {
  display: block;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.radio-card input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.radio-face {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
  text-align: left;
  transition: color 0.3s ease;
}

body.lang-en .radio-face {
  font-size: 17px;
}

/* Uneven radii so the circle reads as drawn by hand, not by a browser. */
.radio-face::before {
  content: '';
  flex: none;
  width: 15px;
  height: 15px;
  border: 1px solid rgba(72, 69, 61, 0.5);
  border-radius: 52% 46% 55% 47%;
  transform: rotate(-8deg);
  background-image: radial-gradient(circle, var(--display) 0, var(--display) 40%, transparent 46%);
  background-size: 0 0;
  background-position: center;
  background-repeat: no-repeat;
  transition: background-size 0.3s var(--ease-out), border-color 0.3s ease;
}

.radio-card input[type="radio"]:checked + .radio-face {
  color: var(--display);
}

.radio-card input[type="radio"]:checked + .radio-face::before {
  border-color: var(--display);
  background-size: 100% 100%;
}

.radio-card input[type="radio"]:focus-visible + .radio-face {
  text-decoration: underline dotted;
}

.rsvp-guests {
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  pointer-events: none;
  margin-top: -22px;
  transition: opacity 0.4s ease, max-height 0.45s ease, margin-top 0.45s ease;
}

/* Only worth asking how many are coming once somebody has said yes. */
.rsvp-guests.is-open {
  opacity: 1;
  max-height: 220px;
  margin-top: 0;
  pointer-events: auto;
}

.rsvp-guests__label,
.rsvp-wish__label {
  font-size: 12px;
  line-height: 1.6;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  margin-bottom: 10px;
}

body.lang-en .rsvp-guests__label,
body.lang-en .rsvp-wish__label {
  font-size: 15px;
  letter-spacing: 0.04em;
}

.rsvp-guests__row {
  display: flex;
  justify-content: center;
  gap: 14px;
}

/* Numbers sit bare on the paper; the chosen one gets circled in ink. */
.radio-face--num {
  justify-content: center;
  width: 40px;
  height: 40px;
  gap: 0;
  font-family: 'Cormorant Garamond', serif;
  font-size: 21px;
  color: var(--ink-soft);
  border: 1.5px solid transparent;
  border-radius: 54% 44% 50% 48%;
  transform: rotate(-6deg);
  transition: border-color 0.3s ease, color 0.3s ease;
}

.radio-face--num::before {
  content: none;
}

.radio-card input[type="radio"]:checked + .radio-face--num {
  border-color: var(--display);
}

/* Ruled notepaper. background-attachment: local keeps the lines under the
   text as it scrolls, and line-height matches the 28px rule spacing. */
.rsvp-textarea {
  display: block;
  resize: none;
  text-align: left;
  line-height: 28px;
  padding: 0 4px;
  height: 113px;
  background-image: repeating-linear-gradient(to bottom,
    transparent 0, transparent 27px,
    rgba(72, 69, 61, 0.2) 27px, rgba(72, 69, 61, 0.2) 28px);
  background-attachment: local;
}

/* A letterpress stamp: double inked rule, no fill, ink-blue type. */
.rsvp-submit {
  align-self: center;
  position: relative;
  background-color: transparent;
  border: 1px solid rgba(78, 107, 125, 0.7);
  outline: 1px solid rgba(78, 107, 125, 0.3);
  outline-offset: 3px;
  color: var(--display);
  font-family: 'DM Medea Display', serif;
  font-size: 18px;
  letter-spacing: 0.04em;
  padding: 10px 34px 11px;
  margin: 4px 0 2px;
  cursor: pointer;
  border-radius: 2px;
  transition: background-color 0.25s ease, transform 0.2s ease;
}

body.lang-en .rsvp-submit {
  font-family: 'Cormorant Garamond', serif;
  font-size: 20px;
  letter-spacing: 0.12em;
}

.rsvp-submit:active {
  background-color: rgba(78, 107, 125, 0.07);
  transform: scale(0.98);
}

.rsvp-submit:disabled {
  opacity: 0.5;
  cursor: default;
}

.rsvp-status {
  font-family: 'Cormorant Garamond', 'Noto Serif Georgian', serif;
  font-size: 14px;
  color: var(--ink-soft);
  min-height: 20px;
  margin-top: -6px;
}

.rsvp-thankyou,
.rsvp-closed {
  display: none;
  padding: 18px 0 8px;
}

.rsvp-thankyou.visible,
.rsvp-closed.visible {
  display: block;
  animation: fadeInUp 0.8s var(--ease-out) both;
}

.thankyou-text {
  /* Secondary face: the display script is for headlines; a sentence set in
     it gets hard to read and needs a very loose line-height to not clip. */
  font-family: 'DM Medea', 'Noto Serif Georgian', serif;
  font-size: 18px;
  color: var(--display);
  letter-spacing: 0.02em;
  line-height: 1.75;
}

body.lang-en .thankyou-text {
  font-family: 'Cormorant Garamond', serif;
  font-size: 22px;
  font-weight: 400;
}

.ty-sub {
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-soft);
  margin-top: 8px;
}

.ty-sub:empty {
  display: none;
}

body.lang-en .ty-sub {
  font-family: 'Cormorant Garamond', serif;
  font-size: 17px;
}

/* ===== WISH SENT (inside the letter) ===== */
.wish-done {
  display: none;
  padding: 6px 0 4px;
}

.wish-done.visible {
  display: block;
  animation: fadeInUp 0.8s var(--ease-out) both;
}

.wish-done__next {
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin-top: 10px;
}

body.lang-en .wish-done__next {
  font-family: 'Cormorant Garamond', serif;
  font-size: 17px;
}

/* An ink underline rather than a button: it is a pointer down the page. */
.wish-done__link {
  margin-top: 14px;
  background: none;
  border: none;
  border-bottom: 1px solid rgba(78, 107, 125, 0.45);
  padding: 2px 2px 3px;
  font-family: 'DM Medea Display', serif;
  font-size: 17px;
  color: var(--display);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

body.lang-en .wish-done__link {
  font-family: 'Cormorant Garamond', serif;
  font-size: 19px;
}

/* The paper texture only starts loading once the loading screen has
   cleared (script.js adds .assets-ready), so it never competes with the
   intro clip and the soundtrack on a slow connection. */
html.assets-ready .env-letter,
html.assets-ready .reply-card {
  background-image: url('paper.jpg');
}

/* ===== RSVP CARD =====
   Same handmade paper as the letter in the envelope, so the reply reads as
   the second sheet of the same stationery. */
.reply-card {
  position: relative;
  max-width: 360px;
  margin: 0 auto;
  padding: 34px 24px 28px;
  background-color: var(--card);
  background-size: cover;
  background-position: center;
  border-radius: 4px;
  box-shadow:
    0 12px 28px rgba(78, 107, 125, 0.12),
    inset 0 0 26px rgba(160, 135, 95, 0.14);
}

.reply-card::before {
  content: '';
  position: absolute;
  inset: 8px;
  border: 1px solid rgba(194, 166, 121, 0.45);
  border-radius: 2px;
  pointer-events: none;
}

/* Lit up when a guest who just wrote a wish is sent down here, so it is
   obvious which card still needs filling in. */
.reply-card.is-prompted {
  animation: replyGlow 2.4s ease-in-out 2;
}

@keyframes replyGlow {
  0%, 100% {
    box-shadow:
      0 12px 28px rgba(78, 107, 125, 0.12),
      inset 0 0 26px rgba(160, 135, 95, 0.14);
  }
  50% {
    box-shadow:
      0 0 0 4px rgba(194, 166, 121, 0.35),
      0 12px 34px rgba(194, 166, 121, 0.35),
      inset 0 0 26px rgba(160, 135, 95, 0.14);
  }
}

.reply-note {
  display: none;
  position: relative;
  align-items: flex-start;
  justify-content: center;
  gap: 8px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--display);
  text-align: left;
  padding: 10px 12px;
  margin: -6px 0 22px;
  border: 1px dashed rgba(78, 107, 125, 0.35);
  border-radius: 3px;
  background-color: rgba(253, 251, 246, 0.55);
}

.reply-note.visible {
  display: flex;
  animation: fadeInUp 0.7s var(--ease-out) both;
}

/* A small inked tick, drawn as two borders of a rotated box. */
.reply-note__tick {
  flex: none;
  width: 6px;
  height: 11px;
  margin: 3px 2px 0 4px;
  border-right: 1.5px solid var(--display);
  border-bottom: 1.5px solid var(--display);
  transform: rotate(40deg);
}

/* ===== RSVP CARD STRUCTURE =====
   Each part of the card is introduced by a small question in the sea blue,
   like the printed prompts on a classic reply card. */
.rsvp-form--card {
  gap: 0;
}

.rsvp-q {
  font-size: 13px;
  line-height: 1.6;
  letter-spacing: 0.04em;
  color: var(--sea);
  margin-bottom: 10px;
}

body.lang-en .rsvp-q {
  font-family: 'Cormorant Garamond', serif;
  font-size: 16px;
  letter-spacing: 0.06em;
}

.rsvp-form--card .rsvp-radios {
  margin-top: 4px;
}

/* The ornament inside the block supplies the spacing above it. */
.rsvp-form--card .rsvp-guests {
  margin-top: 0;
}

.rsvp-form--card .rsvp-guests.is-open {
  margin-top: 0;
  max-height: 260px;
}

.rsvp-form--card .rsvp-submit {
  margin-top: 28px;
}

.rsvp-form--card .rsvp-status {
  margin-top: 12px;
}

/* "Reply for someone else": shared family phones must not be stuck on the
   first person's thank-you. */
.rsvp-another {
  margin-top: 14px;
  background: none;
  border: none;
  border-bottom: 1px solid rgba(138, 133, 122, 0.45);
  padding: 1px 1px 2px;
  font-family: inherit;
  font-size: 13px;
  color: var(--ink-soft);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

body.lang-en .rsvp-another {
  font-family: 'Cormorant Garamond', serif;
  font-size: 16px;
}

/* Fine gold rule with a diamond, splitting the card into its parts. */
.card-orn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin: 24px 0 20px;
}

.card-orn span {
  width: 34px;
  height: 1px;
  background-color: var(--gold);
  opacity: 0.55;
}

.card-orn i {
  width: 5px;
  height: 5px;
  border: 1px solid var(--gold);
  transform: rotate(45deg);
}

.rsvp-thankyou .card-orn {
  margin: 0 0 16px;
}

body.lang-en .reply-note {
  font-family: 'Cormorant Garamond', serif;
  font-size: 16px;
}

/* ===== FOOTER ===== */
.footer-love {
  font-size: 15px;
  color: var(--ink-soft);
  letter-spacing: 0.06em;
  margin-bottom: 10px;
}

.footer-names {
  font-family: 'DM Medea Display', serif;
  font-size: 24px;
  color: var(--display);
  letter-spacing: 0.03em;
}

body.lang-en .footer-names {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
}

.footer-rule {
  width: 34px;
  height: 1px;
  background-color: var(--rule);
  margin: 26px auto 24px;
}

.footer-credit {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  opacity: 0.3;
  transition: opacity 0.25s ease;
}

.footer-credit:hover {
  opacity: 1;
}

/* weddsite.png is black; this filter tints it to roughly --display. */
.footer-logo {
  width: 22px;
  height: auto;
  filter: invert(38%) sepia(18%) saturate(760%) hue-rotate(160deg) brightness(92%);
}

.footer-credit span {
  font-family: 'Cormorant Garamond', serif;
  font-size: 13px;
  letter-spacing: 0.08em;
  color: var(--display);
}

/* ===== FALLING PETALS ===== */
#petals-layer {
  position: absolute;
  inset: 0;
  z-index: 20;
  pointer-events: none;
  overflow: hidden;
  visibility: hidden;
}

#petals-layer.visible {
  visibility: visible;
}

.petal {
  position: absolute;
  pointer-events: none;
  will-change: transform, opacity;
  user-select: none;
  -webkit-user-select: none;
}

/* Petal keyframes use the pinned --vh: plain vh would jump whenever an in-app
   browser slides its nav bar. */
@keyframes petalFallA {
  0%   { transform: translate3d(0, -100px, 0) rotate(0deg);      opacity: 0; }
  12%  { opacity: 0.6; }
  30%  { transform: translate3d(28px, calc(var(--vh) * 26), 0) rotate(50deg); }
  55%  { transform: translate3d(-14px, calc(var(--vh) * 54), 0) rotate(96deg); }
  80%  { transform: translate3d(24px, calc(var(--vh) * 80), 0) rotate(148deg); opacity: 0.5; }
  93%  { opacity: 0; }
  100% { transform: translate3d(-4px, calc(var(--vh) * 110), 0) rotate(183deg); opacity: 0; }
}

@keyframes petalFallB {
  0%   { transform: translate3d(0, -100px, 0) rotate(16deg);     opacity: 0; }
  12%  { opacity: 0.55; }
  32%  { transform: translate3d(-26px, calc(var(--vh) * 28), 0) rotate(-44deg); }
  58%  { transform: translate3d(18px, calc(var(--vh) * 58), 0) rotate(-90deg); }
  82%  { transform: translate3d(-10px, calc(var(--vh) * 82), 0) rotate(-145deg); opacity: 0.45; }
  93%  { opacity: 0; }
  100% { transform: translate3d(6px, calc(var(--vh) * 110), 0) rotate(-176deg); opacity: 0; }
}

/* ===== SHARED ANIMATIONS ===== */
@keyframes introFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes fadeInUp {
  from { opacity: 0; transform: translate3d(0, 20px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes fadeInUpCentered {
  from { opacity: 0; transform: translateX(-50%) translateY(20px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* Runs FROM a soft glow with `backwards` fill; the resting state is plain
   CSS on the revealed selectors. The glyph starts transparent inside a
   blurred shadow of its own colour, then the ink sharpens in. The shadow
   list pads to the element's own text-shadow (the hero's white halo), so it
   hands over smoothly. */
@keyframes wordIn {
  from { opacity: 0; transform: translate3d(0, 0.3em, 0); }
}

@keyframes inkIn {
  from {
    opacity: 0;
    color: rgba(var(--glyph-rgb), 0);
    text-shadow: 0 0 12px rgba(var(--glyph-rgb), 0.9);
    transform: translate3d(0, 0.35em, 0);
  }
}

@keyframes ruleGrow {
  from { opacity: 0; transform: scaleX(0); }
  to   { opacity: 1; transform: scaleX(1); }
}

@keyframes chevronBounce {
  0%   { transform: translateY(-2px) rotate(45deg); opacity: 0.85; }
  50%  { transform: translateY(2px)  rotate(45deg); opacity: 0.35; }
  100% { transform: translateY(-2px) rotate(45deg); opacity: 0.85; }
}

@keyframes monoFloat {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -5px, 0); }
}

@keyframes digitIn {
  from { transform: translate3d(0, 100%, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}

@keyframes digitOut {
  from { transform: translate3d(0, 0, 0); opacity: 1; }
  to   { transform: translate3d(0, -100%, 0); opacity: 0; }
}

@keyframes dotPop {
  0%   { transform: scale(0); }
  60%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}

@keyframes envBob {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  50%      { transform: translate3d(0, -7px, 0) rotate(-0.6deg); }
}

@keyframes hintBreath {
  0%, 100% { opacity: 0.55; transform: translate3d(0, 0, 0); }
  50%      { opacity: 1;    transform: translate3d(0, 4px, 0); }
}

/* ===== BREAKPOINTS ===== */
@media (min-width: 481px) {
  .h-names        { font-size: var(--fs-names-lg); }
  .section-title  { font-size: var(--fs-title-lg); }
  .sheet          { padding: 78px 34px 72px; }
  .cd-num         { font-size: 40px; }
  .cd-unit        { min-width: 78px; padding: 0 18px; }
  .tt-card__body  { padding: 4px 30px 34px; }
  .env-stage      { max-width: 380px; }
  .footer-names   { font-size: 28px; }
}

/* ===== DESKTOP NOTICE ===== */
#desktop-notice {
  display: none;
}

@media (min-width: 769px) {
  body > *:not(#desktop-notice) {
    display: none !important;
  }

  #desktop-notice {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background-color: var(--paper);
    padding: 40px 24px;
    gap: 10px;
  }

  .desktop-notice__logo {
    width: 52px;
    height: auto;
    opacity: 0.8;
    margin-bottom: 24px;
  }

  .desktop-notice__ka,
  .desktop-notice__en {
    font-family: 'Cormorant Garamond', 'Noto Serif Georgian', serif;
    font-size: 18px;
    color: var(--ink);
    letter-spacing: 0.02em;
    text-align: center;
    line-height: 1.5;
  }

  .desktop-notice__sub {
    font-size: 14px;
    color: var(--ink-soft);
    margin-top: 2px;
  }
}

/* ===== REDUCED MOTION ===== */
@media (prefers-reduced-motion: reduce) {
  .tt-card__img img,
  .dress-art img {
    opacity: 1;
    filter: none;
    transform: none;
    transition: none;
  }

  .petal,
  .scroll-chevron,
  .envelope,
  .env-hint,
  .hero.revealed .hero-still,
  .hero.revealed .hero-shimmer,
  .hero.revealed .ch,
  .thanks-text.visible .w,
  .reply-card.is-prompted {
    animation: none !important;
  }

  .reveal,
  .ch,
  .w {
    opacity: 1;
    transform: none;
  }
}
