/* Weddsite Pro-12 ("Sun & Moon Envelope") styles. Mobile-first, min-width
   queries only (481 / 769). Class names are shared with script.js and
   index.html; the % values on .env* / .hero__text are measured on the 941x1672
   paintings and must move with them if the art is swapped.

   Radius scale (one rule, used everywhere): paper sheets 30px lip, cards and
   the letter 18px, inputs 12px, buttons / pills / hint fully round. */

/* ==========================================================================
   1. FONTS. block, not swap: the loader covers the block period, and a swap
   would repaint the handwritten names mid-write.
   ========================================================================== */
@font-face {
  font-family: 'DM Medea';
  src: url('fonts/medea.woff2') format('woff2');
  font-weight: 400;
  font-display: block;
}
/* Pinyon Script, cut down to "&" and 0-9 (_tools/prep.py): DM Medea has no
   ampersand, and its loops match Medea's far better than a text face does.
   The digits set the countdown. */
@font-face {
  font-family: 'Pinyon';
  src: url('fonts/pinyon.woff2') format('woff2');
  font-weight: 400;
  font-display: block;
}
@font-face {
  font-family: 'BPG Classic';
  src: url('fonts/bpg-classic.woff2') format('woff2');
  font-weight: 400;
  font-display: block;
}

/* ==========================================================================
   2. TOKENS. Sampled from the paintings: the hero's paper, its sky, the
   envelope's red and the gold of the painted stars.
   ========================================================================== */
:root {
  --vh: 1vh;                    /* JS off = plain vh; the head script pins it */

  --paper:   #f7f2e8;
  --paper-2: #f1e8da;
  --paper-3: #eadccb;
  --sky:     #e2f1f4;
  --dusk:    #f4e3e1;

  --wine:      #8e1b2b;
  --wine-deep: #6c1421;
  --rose:      #c75a6b;
  --envelope:  #8e272c;
  --gold:      #c8901f;
  --sage:      #6f8a4c;

  --ink:      #4a2b2d;
  --ink-soft: #7d5f5f;
  --line:     #e2cfc0;

  /* Script for the names, section titles and sign-off; BPG Classic for
     everything a guest has to read; Pinyon for the "&" and countdown digits. */
  --f-script: 'DM Medea', 'BPG Classic', serif;
  --f-text:   'BPG Classic', serif;
  --f-amp:    'Pinyon', 'DM Medea', serif;

  --lip: 30px;                  /* sheet radius AND its overlap onto the one above */
  --tick: 83ms;                 /* one 12fps frame, the clock all stop motion steps on */

  /* The paintings are 941x1672. Covering the screen with that box: */
  --art-ratio: 0.5628;          /* width / height */
  --art-inv:   1.77683;         /* height / width */
}

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

html {
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: none;
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
  transition: scrollbar-color 1.2s ease;
}
/* The thumb fades in only once the envelope has handed over to the page. */
html.is-open { scrollbar-color: var(--rose) var(--paper-2); }
html::-webkit-scrollbar { width: 7px; }
html::-webkit-scrollbar-track { background-color: var(--paper-2); }
html::-webkit-scrollbar-thumb {
  border-radius: 7px;
  background-color: transparent;
  transition: background-color 1.2s ease;
}
html.is-open::-webkit-scrollbar-thumb { background-color: var(--rose); }

body {
  overscroll-behavior-y: none;
  background-color: var(--paper);
  color: var(--ink);
  font-family: var(--f-text);
  font-size: 17px;
  line-height: 1.7;
  -webkit-tap-highlight-color: transparent;
}

/* Intro lock. NEVER position:fixed / overflow:hidden on html/body: Chrome iOS
   opened from a chat app freezes its viewport short when the document stops
   being scrollable. The gesture is trapped in script.js instead. */
html.is-locked, body.is-locked { overscroll-behavior: none; touch-action: none; }

img { display: block; max-width: 100%; height: auto; }
a { color: inherit; }
button, input { font: inherit; color: inherit; }

.page { overflow-x: clip; }

/* ==========================================================================
   4. LOADER. Out-ranks the envelope (10000).
   ========================================================================== */
.loader {
  position: fixed;
  inset: 0;
  z-index: 10001;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--paper);
  transition: opacity 0.6s ease;
}
.loader.is-done { opacity: 0; pointer-events: none; }
.loader.is-gone { display: none; }
.loader__ring {
  width: 38px;
  height: 38px;
  border: 2px solid var(--line);
  border-top-color: var(--wine);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   5. ENVELOPE OPENING
   ========================================================================== */
.intro {
  position: fixed;
  inset: 0;                     /* never a forced height: inset reaches the real glass */
  z-index: 10000;
  overflow: hidden;
  background-color: var(--paper);
  cursor: pointer;
  outline: none;
  transition: opacity 0.7s ease;
}
.intro.is-gone { opacity: 0; pointer-events: none; }

/* Same cover box as .hero__stage, centred the same way, so the zoomed card
   and the hero coincide. translate centres it; transform is free for the
   stop-motion nudges script.js gives the whole envelope. */
.intro__stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100vw, calc(var(--vh) * 100 * var(--art-ratio)));
  height: max(calc(var(--vh) * 100), calc(100vw * var(--art-inv)));
  translate: -50% -50%;
}

.env {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
.env--closed { z-index: 8; }   /* on top of every layer until the first lift frame hides it */
/* The open plate minus its flap: rows from the back's top edge (470/1672) down. */
.env--body   { z-index: 2; clip-path: inset(28.1% 0 0 0); }
/* The open plate's flap only, hinged on that same edge. Starts folded flat. */
.env--flapo  {
  z-index: 3;
  clip-path: inset(0 0 71.9% 0);
  transform-origin: 50% 28.1%;
  transform: scaleY(0);
}
/* The pocket's front: envelope-lower.png keyed (see _tools/prep.py), 513px down. */
.env--front  { z-index: 6; top: 30.682%; height: 69.318%; }
/* The closed plate's flap, hinged off the top of the painting, used only for the
   frames where it lifts. Point of the V at 965/1672, corners at 500/1672. */
.env--flapc  {
  z-index: 7;
  clip-path: polygon(0 0, 100% 0, 100% 29.9%, 50% 57.72%, 0 29.9%);
  transform-origin: 50% 0;
  visibility: hidden;
}

/* Sun and moon: placed by script.js in px each 12fps tick (left/top stay 0). */
.orb {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  pointer-events: none;
  will-change: transform;
}
.orb img { width: 100%; height: auto; max-width: none; }
.orb--sun  { width: 41%; }
.orb--moon { width: 33%; }

/* Stars that tumble out of the pocket with the sun and moon. Placed by
   script.js by centre each 12fps tick, like the orbs. */
.spark {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  width: 9%;
  height: auto;
  max-width: none;
  visibility: hidden;
  pointer-events: none;
  will-change: transform;
}

/* The hero card, a full-stage box. transform-origin centre: scale(.78) puts it
   at 11%..89% of the stage, translateY moves it in % of the stage. */
.card {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 5;
  overflow: hidden;
  border-radius: 22px;
  box-shadow: 0 10px 30px rgba(74, 20, 30, 0.28);
  transform: translateY(53%) scale(0.78);
  will-change: transform;
}
.card img { width: 100%; height: 100%; max-width: none; }
/* The camera push. It moves on ones (smooth), not on the 12fps steps: a stepped
   camera reads as judder. Nothing fades. The card comes up to fill the screen
   while the envelope, which stays under it in paint order, is let go: it grows
   with the push (same depth as the card) and drops out of the bottom of the
   frame, so the card slides free of the pocket on its own. Both curves start
   from rest, so the hand-off from the last held drawing has no kick. */
.card.is-zoom {
  transition: transform 2s cubic-bezier(0.55, 0, 0.25, 1), border-radius 2s ease, box-shadow 2s ease;
  transform: none;
  border-radius: 0;
  box-shadow: 0 0 0 rgba(74, 20, 30, 0);
}
.intro.is-zoom .env {
  transform-origin: 50% 50%;                 /* the stage centre: scale like one camera */
  transition: transform 2s cubic-bezier(0.5, 0, 0.35, 1);
}
.intro.is-zoom .env--front { transform-origin: 50% 27.87%; }   /* stage centre, in the front's own box */

/* Centred with auto margins and max-content, never left:50% + translate: an
   absolute box at left:50% only gets the right half of the screen to size
   itself in, which squeezed the capsule narrower than its text. */
.intro__hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(14% + env(safe-area-inset-bottom));
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 10px;
  width: max-content;
  margin: 0 auto;
  padding: 9px 22px;
  border-radius: 999px;
  background-color: rgba(247, 242, 232, 0.94);
  box-shadow: 0 6px 20px rgba(60, 10, 18, 0.3);
  color: var(--wine);
  font-family: var(--f-text);
  font-size: 21px;
  line-height: 1.3;
  white-space: nowrap;
  animation: hint-bob 1.2s steps(1) infinite;
  transition: opacity 0.35s ease;
}
.intro__hint img { flex: none; width: 16px; height: auto; }
.intro.is-opening .intro__hint { opacity: 0; }
html.is-loading .intro__hint { animation-play-state: paused; opacity: 0; }

/* Held frames, not a tween: the hint hops like a cut-out nudged between shots. */
@keyframes hint-bob {
  0%   { transform: translateY(0) rotate(0); }
  25%  { transform: translateY(-4px) rotate(-1.5deg); }
  50%  { transform: translateY(-6px) rotate(1deg); }
  75%  { transform: translateY(-2px) rotate(0.5deg); }
}

/* ==========================================================================
   6. SHARED STOP-MOTION PIECES
   ========================================================================== */

/* Boil: a held drawing re-jitters every two frames, three drawings cycling.
   steps(1) holds each keyframe instead of tweening between them. Uses
   `transform`; parallax uses the separate `translate` property, so both apply. */
.boil { animation: boil calc(var(--tick) * 6) steps(1) infinite; }
.boil--slow { animation-duration: calc(var(--tick) * 9); animation-delay: -0.2s; }
@keyframes boil {
  0%   { transform: translate(0, 0) rotate(0deg); }
  33%  { transform: translate(0.6px, -0.5px) rotate(0.9deg); }
  66%  { transform: translate(-0.5px, 0.4px) rotate(-0.7deg); }
}

/* Placed on held frames: a paper cut-out laid down by hand, overshooting and
   settling across four drawings. Lit by the IntersectionObserver in script.js. */
.pop { opacity: 0; }
.pop.is-in { animation: place calc(var(--tick) * 8) steps(1) var(--d, 0s) both; }
@keyframes place {
  0%   { opacity: 0; transform: translateY(16px) rotate(calc(var(--t, 0deg) - 3deg)) scale(0.95); }
  25%  { opacity: 1; transform: translateY(7px) rotate(calc(var(--t, 0deg) + 2deg)) scale(0.99); }
  50%  { opacity: 1; transform: translateY(-3px) rotate(calc(var(--t, 0deg) - 1deg)) scale(1.02); }
  75%  { opacity: 1; transform: translateY(1px) rotate(calc(var(--t, 0deg) + 0.4deg)) scale(1); }
  100% { opacity: 1; transform: rotate(var(--t, 0deg)); }
}

/* Stars cut from the hero painting, scattered over the sheets. The span is
   placed in % of its sheet via inline --x/--y/--w and carries the parallax; the
   img inside twinkles (--r tilt, --d offsets the twinkle so they never blink
   together). Two elements because parallax and twinkle are separate animations. */
.star {
  position: absolute;
  left: var(--x);
  top: var(--y);
  z-index: 0;
  width: var(--w, 18px);
  pointer-events: none;
}
.star img {
  width: 100%;
  transform: rotate(var(--r, 0deg));
  animation: twinkle calc(var(--tick) * 30) steps(1) var(--d, 0s) infinite;
}
@keyframes twinkle {
  0%   { transform: rotate(var(--r, 0deg)) scale(1); }
  70%  { transform: rotate(calc(var(--r, 0deg) + 10deg)) scale(0.8); }
  80%  { transform: rotate(calc(var(--r, 0deg) - 6deg)) scale(1.18); }
  90%  { transform: rotate(var(--r, 0deg)) scale(1.04); }
}
html:not(.is-open) .star img,
html:not(.is-open) .boil { animation-play-state: paused; }

/* Parallax. [data-par="k"] drifts by k x its distance from the screen's centre:
   k > 0 lags behind the scroll (far away), k < 0 runs ahead (near). Where the
   browser has scroll-driven animations (html.sda, set by script.js) it runs on
   the compositor, in step with the finger, so it can't stutter. Elsewhere
   script.js writes the same `translate` from scrollY. */
html.sda [data-par] {
  animation: par linear both;
  animation-timeline: view();
  animation-range: cover 0% cover 100%;
}
@keyframes par {
  from { translate: 0 calc(var(--k, 0) * var(--vh) * -60); }
  to   { translate: 0 calc(var(--k, 0) * var(--vh) * 60); }
}

/* ==========================================================================
   7. HERO
   Sticky, so it stays exactly where it is while the sheets slide up over it.
   That IS the hero's parallax, and the browser does it on the compositor: no
   JS ever moves the painting, which is what made it judder on scroll.
   ========================================================================== */
.hero {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background-color: #bfe3ec;
}
.vh-lock .hero { height: calc(var(--vh) * 100); }

/* The cover box. Must stay identical to .intro__stage (see there). */
.hero__stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100vw, calc(var(--vh) * 100 * var(--art-ratio)));
  height: max(calc(var(--vh) * 100), calc(100vw * var(--art-inv)));
  translate: -50% -50%;
}
.hero__art { width: 100%; height: 100%; max-width: none; }

/* The cream arch inside the painting: x 24-76%, y 20-66% of the art. Centred a
   little above the middle, where the arch is widest between the palms and the
   flower urns. */
.hero__text {
  position: absolute;
  left: 24%;
  right: 24%;
  top: 20%;
  bottom: 34%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.hero__names {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--f-script);
  font-weight: 400;
  font-size: 42px;
  line-height: 1.25;
  color: var(--wine);
  /* A soft paper halo: the arch is cream, but palm tips and painted stars reach
     into it, and the halo keeps the thin script legible where they do. */
  text-shadow: 0 0 10px rgba(247, 242, 232, 1), 0 0 22px rgba(247, 242, 232, 0.9), 0 0 36px rgba(247, 242, 232, 0.6);
  position: relative;
  isolation: isolate;
}
/* A faint wash of paper behind the whole block, a touch stronger than the
   halo alone. It comes up with the first letter. */
.hero__names::before {
  content: '';
  position: absolute;
  inset: -14% -22%;
  z-index: -1;
  background-image: radial-gradient(closest-side, rgba(247, 242, 232, 0.72), rgba(247, 242, 232, 0.4) 55%, rgba(247, 242, 232, 0));
  opacity: 0;
  transition: opacity 0.9s ease;
}
.hero__names.is-writing::before { opacity: 1; }
/* Each line is split into letters by script.js "Names" (.ltr), each one an
   inline-block exactly as wide as its kerned advance, so the word sets the same
   as unsplit text. Letters are laid down one after another, each in five held
   drawings, at full ink from the first: it pops in small and low, overshoots,
   settles. No faded drawings. Neighbours overlap in
   time (--ld steps 2.5 frames, a letter lasts 10), so the line flows rather
   than ticking. --lr is the line's own small hand-lettered tilt. */
.hero__name,
.hero__amp {
  display: block;
  white-space: nowrap;
  padding: 0 0.12em;
  transform: rotate(var(--lr, 0deg));
}
.hero__amp {
  margin: 0.06em 0 0.02em;
  font-family: var(--f-amp);
  font-size: 1.7em;         /* Pinyon's & is only half an em tall: this brings it up to the names' letters */
  line-height: 0.62;
  color: var(--rose);
}
.ltr {
  display: inline-block;
  transform-origin: 50% 80%;
  opacity: 0;
}
.hero__names.is-writing .ltr {
  /* forwards, not both: during its delay a letter must be simply absent, not
     already showing its first drawing. */
  animation: ink calc(var(--tick) * 10) steps(1) var(--ld, 0s) forwards;
}
@keyframes ink {
  0%   { opacity: 1;    transform: translateY(0.1em) rotate(-7deg) scale(0.72); }
  20%  { opacity: 1;    transform: translateY(0.03em) rotate(-3deg) scale(0.9); }
  40%  { opacity: 1;    transform: translateY(-0.05em) rotate(3deg) scale(1.1); }
  60%  { opacity: 1;    transform: translateY(0.01em) rotate(-1deg) scale(0.97); }
  80%  { opacity: 1;    transform: rotate(0.4deg) scale(1.02); }
  100% { opacity: 1;    transform: none; }
}

.hero__date {
  margin-top: 36px;         /* clears the second name's descender swash */
  padding: 3px 14px 2px;
  border-radius: 999px;
  /* A paper pill: on short phones the line is wider than the arch and runs
     over the painted hydrangeas, and the pill keeps it legible there. */
  background-color: rgba(247, 242, 232, 0.88);
  font-family: var(--f-text);
  font-size: 17px;
  color: var(--ink);
  white-space: nowrap;
  opacity: 0;
}
.hero__date.is-in { animation: place calc(var(--tick) * 8) steps(1) both; }

.hero__cue {
  position: absolute;
  left: 50%;
  bottom: calc(46px + env(safe-area-inset-bottom));   /* clear of the next sheet's 30px lip */
  z-index: 2;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background-color: rgba(247, 242, 232, 0.9);
  box-shadow: 0 4px 14px rgba(74, 20, 30, 0.18);
  color: var(--wine);
  translate: -50% 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}
.hero__cue svg { width: 20px; height: 20px; fill: currentColor; }
.hero__cue.is-in {
  opacity: 1;
  pointer-events: auto;
  animation: hint-bob 1.6s steps(1) infinite;
}

/* The sun and moon hanging at the top centre. They drop in from above the
   screen with a soft overshoot (script.js adds .is-in once the card has
   settled), then sway forever, each on its own slow period so they never move
   in unison. Swing is from the top, like something hung on a thread. */
.hero__sky {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(env(safe-area-inset-top) + 14px);
  z-index: 2;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 2px;
  pointer-events: none;
}
.hang {
  display: block;
  transform: translateY(calc(-100% - 40px));
  transition: transform 1.5s cubic-bezier(0.34, 1.35, 0.5, 1);
}
.hang.is-in { transform: none; }
.hang--sun  { width: 128px; }
.hang--moon { width: 104px; margin-top: 12px; transition-delay: 0.22s; }
.hang img {
  width: 100%;
  transform-origin: 50% -30%;
  animation: sway 5.2s ease-in-out infinite;
}
.hang--moon img { animation-duration: 6.4s; animation-delay: -2.1s; }
@keyframes sway {
  0%, 100% { transform: rotate(-3.5deg) translateY(0); }
  50%      { transform: rotate(3.5deg) translateY(3px); }
}
html:not(.is-open) .hang img { animation-play-state: paused; }

/* ==========================================================================
   8. SHEETS. Every section below the hero is a sheet of paper laid over the
   one before it: a rounded lip, an upward shadow and a negative margin so it
   rides over its neighbour. Paint order is DOM order, later on top, which is
   exactly what the overlap needs. Each sheet is opaque: the separation is the
   fold, the shadow and the step in paper tone, never transparency.
   ========================================================================== */
.sheet {
  position: relative;
  z-index: 2;
  margin-top: calc(var(--lip) * -1);
  padding: 58px 22px 70px;
  border-top-left-radius: var(--lip);
  border-top-right-radius: var(--lip);
  background-color: var(--paper);
  box-shadow: 0 -14px 32px rgba(74, 20, 30, 0.14);
  overflow: hidden;
  /* clip, not hidden, where supported: a hidden box is a scroll container, and
     the parallax's view() timeline would bind to the sheet instead of the page. */
  overflow: clip;
}
/* Light caught along the lip, so the fold reads even where tones are close. */
.sheet::before {
  content: '';
  position: absolute;
  top: 0;
  left: 11%;
  right: 11%;
  height: 1px;
  background-color: rgba(255, 255, 255, 0.85);
}

.sheet__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 380px;
  margin: 0 auto;
  text-align: center;
}

.title {
  font-family: var(--f-script);
  font-weight: 400;
  font-size: 36px;
  line-height: 1.7;         /* a wrapped title's tall swashes must not reach the line above */
  color: var(--wine);
  margin-bottom: 8px;       /* Medea's descender swashes need air before the next line */
}

.lead {
  max-width: 32ch;
  margin: 16px auto 0;
  font-size: 18px;
  line-height: 1.75;
  color: var(--ink);
}

/* ---- Invitation + countdown ---- */
.duo {
  position: relative;
  width: 132px;
  height: 78px;
  margin: 0 auto 14px;
}
.duo__sun  { position: absolute; left: 0;  top: 0; width: 86px; }
.duo__moon { position: absolute; right: 0; top: 10px; width: 64px; }

.count__label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 40px;
  font-family: var(--f-text);
  font-size: 19px;
  color: var(--ink-soft);
}
.count__label img { width: 15px; height: auto; }
.count {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin-top: 22px;
}
/* Paper tags hung on a pin, each at its own small tilt (--t) like cut-outs on a
   desk. The dashed inner line is a stitch. Rotation is on the tag; the pin
   (::before / ::after: a tack and its shadow) rides with it. */
.count__tag {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 4px 12px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background-color: #fffaf1;
  box-shadow: 0 6px 14px rgba(74, 20, 30, 0.09);
  outline: 1px dashed rgba(199, 90, 107, 0.35);
  outline-offset: -6px;
  transform: rotate(var(--t, 0deg));
}
.count__pin {
  position: absolute;
  left: 50%;
  top: -5px;
  width: 11px;
  height: 11px;
  margin-left: -5.5px;
  border-radius: 50%;
  background-color: var(--rose);
  box-shadow: inset -2px -2px 0 rgba(108, 20, 33, 0.35), 0 2px 3px rgba(74, 20, 30, 0.25);
}
.count__num {
  font-family: var(--f-amp);
  font-size: 36px;
  line-height: 1.1;
  color: var(--wine);
  font-variant-numeric: tabular-nums;
}
.count__num.is-hop { animation: hop calc(var(--tick) * 4) steps(1); }
@keyframes hop {
  0%  { transform: translateY(-5px) rotate(-4deg); }
  50% { transform: translateY(-2px) rotate(2deg); }
}
.count__unit {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.2;
  color: var(--ink-soft);
}

/* ---- Day plan ---- */
.sheet--plan {
  /* The one gradient on the page: day at the top, dusk at the bottom. */
  background-color: var(--sky);
  background-image: linear-gradient(to bottom, var(--sky) 0%, #eef0ea 52%, var(--dusk) 100%);
  padding-bottom: 96px;
}
.sky-orb {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  opacity: 0.9;
}
.sky-orb img { width: 100%; }
.sky-orb--sun  { top: 30px; right: -46px; width: 138px; }
.sky-orb--moon { bottom: 40px; left: -40px; width: 112px; }

.plan {
  position: relative;
  list-style: none;
  margin-top: 34px;
  text-align: left;
}
/* The path between markers: a dashed thread, drawn behind them. */
.plan::before {
  content: '';
  position: absolute;
  left: 27px;
  top: 30px;
  bottom: 40px;
  border-left: 2px dashed rgba(142, 27, 43, 0.28);
}
.event {
  position: relative;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 14px;
  align-items: start;
  transform: rotate(var(--t, 0deg));
}
.event + .event { margin-top: 22px; }
.event__mark {
  position: relative;
  z-index: 1;
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 8px;
  border-radius: 50%;
  background-color: #fffaf1;
  box-shadow: 0 3px 10px rgba(74, 20, 30, 0.12);
}
.event__mark img { width: 44px; height: auto; }
.event__mark--star img { width: 28px; }
.event__mark--moon img { width: 38px; }
.event__card {
  padding: 16px 18px 15px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background-color: #fffaf1;
  box-shadow: 0 6px 18px rgba(74, 20, 30, 0.08);
}
.event__time {
  font-family: var(--f-text);
  font-size: 26px;
  line-height: 1.1;
  color: var(--wine);
}
.event__name {
  margin-top: 4px;
  font-weight: 400;
  font-size: 19px;
  line-height: 1.35;
  color: var(--ink);
}
.event__place {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  margin-top: 4px;
  font-size: 15px;
  color: var(--rose);
  text-decoration: none;
  transition: color 0.3s ease;
}
.event__place svg { flex: none; width: 17px; height: 17px; fill: currentColor; }
.event__place span { border-bottom: 1px solid rgba(199, 90, 107, 0.35); }
.event__place:active { color: var(--wine); }

/* ---- RSVP: the back of the envelope with the reply letter in it ---- */
.sheet--rsvp {
  padding: 56px 18px 90px;
  background-color: var(--envelope);
  background-image: url('img/env-closed.webp');
  background-size: cover;
  background-position: center top;
}
.letter {
  position: relative;
  max-width: 400px;
  margin: 0 auto;
  padding-top: 64px;
}
.letter__peek {
  position: absolute;
  top: 0;
  z-index: 0;
  pointer-events: none;
}
.letter__peek--sun  { left: -6px;  width: 118px; }
.letter__peek--moon { right: -2px; top: 10px; width: 96px; }
.letter__paper {
  position: relative;
  z-index: 1;
  padding: 34px 22px 30px;
  border-radius: 18px;
  background-color: var(--paper);
  box-shadow: 0 18px 40px rgba(40, 6, 12, 0.35);
  text-align: center;
}
.letter__paper .title { font-size: 31px; line-height: 1.4; }   /* the longest title: must fit the card at 320px */
/* Two words, always on two lines, set apart and staggered like hand lettering.
   Medea's ტ and ფ have tall loops that collided between tightly set lines; the
   gap and the stagger keep every loop clear of the line above. */
.title--two { margin-top: 0.4em; }          /* ფ's loop clears the sun and moon above */
.title--two span { display: block; line-height: 1.5; }
.title--two span:first-child { margin-right: 0.7em; }
.title--two span + span { margin-top: 0.62em; margin-left: 0.7em; }
.rsvp__deadline {
  margin-top: 22px;         /* below the title's ყ tail */
  font-size: 15px;
  color: var(--ink-soft);
}
.rsvp__form { margin-top: 24px; text-align: left; }

.field { border: 0; margin-bottom: 18px; min-width: 0; }
.field__label {
  display: block;
  margin-bottom: 7px;
  padding: 0;
  font-size: 15px;
  line-height: 1.4;
  color: var(--ink);
}
.field__input {
  width: 100%;
  height: 48px;
  padding: 0 14px;
  border: 1px solid #cdb6a6;
  border-radius: 12px;
  background-color: #fffcf6;
  font-size: 16px;          /* 16px+ keeps iOS from zooming on focus */
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.field__input:focus {
  outline: none;
  border-color: var(--wine);
  box-shadow: 0 0 0 3px rgba(142, 27, 43, 0.15);
}
.field.is-invalid .field__input { border-color: #b3261e; }

.choice { display: grid; gap: 10px; }
.choice__card { position: relative; display: block; cursor: pointer; }
.choice__card input,
.pill input { position: absolute; opacity: 0; pointer-events: none; }
.choice__card span {
  display: block;
  padding: 12px 16px;
  border: 1px solid #cdb6a6;
  border-radius: 999px;
  background-color: #fffcf6;
  font-size: 16px;
  text-align: center;
  transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}
.choice__card input:checked + span {
  border-color: var(--wine);
  background-color: var(--wine);
  color: #fff8ef;
}
.choice__card input:focus-visible + span,
.pill input:focus-visible + span { box-shadow: 0 0 0 3px rgba(142, 27, 43, 0.25); }

.rsvp__count { transition: opacity 0.3s ease; }
.rsvp__count.is-off { display: none; }
.pills { display: flex; gap: 8px; }
.pill { flex: 1; cursor: pointer; }
.pill span {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  border: 1px solid #cdb6a6;
  border-radius: 999px;
  background-color: #fffcf6;
  font-family: var(--f-text);
  font-size: 18px;
  transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}
.pill input:checked + span {
  border-color: var(--wine);
  background-color: var(--wine);
  color: #fff8ef;
}

.rsvp__error {
  min-height: 1.4em;
  margin: -4px 0 10px;
  font-size: 14px;
  color: #b3261e;
}
.btn {
  display: block;
  width: 100%;
  height: 52px;
  border: 0;
  border-radius: 999px;
  background-color: var(--wine);
  color: #fff8ef;
  font-family: var(--f-text);
  font-size: 19px;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(108, 20, 33, 0.3);
  transition: background-color 0.25s ease, transform 0.15s ease;
}
.btn:active { transform: translateY(1px) scale(0.99); background-color: var(--wine-deep); }
.btn:disabled { opacity: 0.7; cursor: default; }

.rsvp__thanks { display: none; padding: 26px 0 8px; }
.rsvp__thanks-star { width: 40px; margin: 0 auto 12px; }
.rsvp__thanks-text { font-size: 18px; line-height: 1.7; color: var(--ink); }
.letter__paper.is-sent .rsvp__form { display: none; }
.letter__paper.is-sent .rsvp__thanks {
  display: block;
  animation: place calc(var(--tick) * 8) steps(1) both;
}

.rsvp__closed { display: none; margin-top: 22px; font-family: var(--f-text); font-size: 20px; color: var(--wine); }
.letter__paper.is-closed .rsvp__form,
.letter__paper.is-closed .rsvp__deadline { display: none; }
.letter__paper.is-closed .rsvp__closed { display: block; }

/* ---- Footer ---- */
.sheet--foot {
  padding: 46px 22px calc(34px + env(safe-area-inset-bottom));
  background-color: var(--paper-3);
  text-align: center;
}
.foot__names { font-family: var(--f-script); font-size: 36px; line-height: 1.5; color: var(--wine); }
.foot__date { margin-top: 2px; font-family: var(--f-text); font-size: 16px; color: var(--ink-soft); }
.credit {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 26px;
  font-family: var(--f-text);
  font-size: 13px;
  color: var(--wine);
  text-decoration: none;
  opacity: 0.3;
  transition: opacity 0.3s ease;
}
.credit:hover, .credit:focus-visible { opacity: 0.75; }
.credit__logo { width: 18px; height: 18px; }

/* ---- Desktop notice ---- */
.desktop-notice { display: none; }

/* ==========================================================================
   9. BREAKPOINTS
   ========================================================================== */
@media (min-width: 481px) {
  .hero__names { font-size: 64px; }
  .hang--sun  { width: 146px; }
  .hang--moon { width: 118px; }
  .hero__date { font-size: 19px; }
  .title { font-size: 40px; }
  .count__num { font-size: 44px; }
  .sky-orb--sun { width: 170px; }
}

@media (min-width: 769px) {
  .desktop-notice {
    position: fixed;
    inset: 0;
    z-index: 30000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background-color: var(--paper);
    text-align: center;
  }
  .desktop-notice p { font-size: 16px; line-height: 1.9; color: var(--ink); }
  .desktop-notice__logo { width: 52px; height: auto; opacity: 0.8; margin-bottom: 18px; }
  .desktop-notice__sub { margin-top: 6px; font-size: 14px; color: var(--ink-soft) !important; }
}

/* ==========================================================================
   10. REDUCED MOTION. Loops stop, placements are instant, the envelope is
   skipped straight to the page (script.js), names appear whole.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .boil, .star img, .hang img, .intro__hint, .hero__cue.is-in, html.sda [data-par] { animation: none; }
  .hang { transition: none; }
  .pop, .pop.is-in, .hero__date, .hero__date.is-in { opacity: 1; animation: none; }
  .ltr, .hero__names.is-writing .ltr { opacity: 1; animation: none; }
  .hero__names::before { opacity: 1; transition: none; }
  .count__num.is-hop, .letter__paper.is-sent .rsvp__thanks { animation: none; }
}
