/* ════════════════════════════════════════
   ANNIVERSARY - SLOT MACHINE INVITATION
   Mobile-first. Breakpoints: 481 / 769 / 1025
════════════════════════════════════════ */

/* Aladini - latin keyboard in, georgian letterforms out.
   Latin-only cmap: real georgian text falls back to Noto Serif Georgian,
   and real english text must be overridden to a latin face (see .credit). */
@font-face {
  font-family: "Aladini";
  src: url("aladini.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

:root {
  --cream: #fff7f3;
  --blush: #ffe9ee;
  --blush-deep: #ffd9e2;
  --pink-soft: #f9c9d6;
  --pink: #f5a8bd;
  --pink-deep: #d9718f;
  --rose-dark: #b05575;

  --gold-lt: #ffe6cf;
  --gold: #f6c79d;
  --gold-mid: #e7ae8e;
  --gold-deep: #b4705c;

  --plum: #6a4957;
  --plum-lt: #846879;
  --text: #7c5262;
  --text-soft: #a97e8c;

  --yellow: #ffd23f;
  --screen: #fdf4f1;
  --screen-lit: #fffaf3;

  /* 1% of the viewport height in pixels, frozen by the head script.
     See the VIEWPORT HEIGHT note below. */
  --vh: 1vh;
}

/* ────────────────────────────────────────
   VIEWPORT HEIGHT - the in-app browser jump

   Instagram / Messenger / Facebook slide a bottom nav bar in and out while the
   guest scrolls. That bar is a NATIVE view sitting outside the webview: when
   it appears, the app resizes the webview's whole frame. The page is not told
   "some chrome appeared" - it is told the window got shorter.

   Which means `svh` does not help here. `svh` / `lvh` / `dvh` describe the
   browser's OWN retractable chrome, and an in-app webview has none as far as
   it knows - so all three units recompute together off the new frame, exactly
   like plain `vh`. There is no CSS unit that survives this. Anything sized
   against the viewport is re-measured mid-scroll and the page shifts under
   the guest's thumb.

   So the height is pinned in JavaScript instead. `--vh` is measured before
   first paint (inline script in index.html) and then rewritten ONLY when the
   viewport WIDTH changes - rotation, or a desktop resize. A resize with an
   unchanged width can only be that nav bar, and is ignored on purpose. That
   is the whole fix.

   The cascade, in the order it must stay:

     min-height: 100vh;             ← floor for engines without svh
     min-height: 100svh;            ← better floor, and correct in real Safari
                                      / Chrome where the chrome IS the browser's
     .vh-lock … calc(var(--vh)*100) ← the pin. MUST come last and win.

   The pin lives on a `.vh-lock` class rather than in the declarations above so
   that a browser with JS disabled keeps the `svh` fallback, instead of hitting
   an unresolved var() and collapsing the section to zero height.

   Never reintroduce `dvh` in this file - it tracks the bar by definition and
   repaints on every frame of the slide.
──────────────────────────────────────── */

.vh-lock body,
.vh-lock .hero,
.vh-lock .shot,
.vh-lock .plan,
.vh-lock .rsvp {
  min-height: calc(var(--vh) * 100);
}

/* the machine's height is the one that hurts most - the reel strips are
   measured off its rendered size, so a resize here rebuilds the slot machine
   mid-scroll. --mh is the per-breakpoint number, declared on .machine__img. */
.vh-lock .machine__img {
  height: calc(var(--vh) * var(--mh));
}

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

/* stops the rubber-band overscroll that makes the toolbar flap on every flick
   at the top or bottom of the page. Declared on html only: paired with an
   overflow-x on body it would trap the trackpad on a dead body scroller and
   refuse to chain the scroll out to html. */
html {
  overscroll-behavior-y: none;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  min-height: 100vh;
  min-height: 100svh;
  font-family: "Aladini", "Noto Serif Georgian", Georgia, serif;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
  background-color: var(--blush);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

/* Scrollbar - blush + rose */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(217, 113, 143, 0.45) rgba(255, 217, 226, 0.6);
}

*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

*::-webkit-scrollbar-track {
  background: rgba(255, 217, 226, 0.6);
}

*::-webkit-scrollbar-thumb {
  background: rgba(217, 113, 143, 0.45);
  border-radius: 20px;
  border: 2px solid rgba(255, 217, 226, 0.6);
}

*::-webkit-scrollbar-thumb:hover {
  background: rgba(217, 113, 143, 0.7);
}

img {
  max-width: 100%;
}

/* ────────────────────────────────────────
   DESKTOP NOTICE
──────────────────────────────────────── */
.mobile-prompt {
  display: none;
}

@media (min-width: 769px) {
  .mobile-prompt {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    width: 340px;
    padding: 22px 20px 20px;
    background-color: rgba(255, 255, 255, 0.96);
    border: 1px solid rgba(217, 113, 143, 0.28);
    border-radius: 18px;
    text-align: center;
    box-shadow: 0 10px 34px rgba(176, 85, 117, 0.22);
    transition: opacity 0.35s ease, transform 0.35s ease;
  }

  .mobile-prompt.hidden {
    opacity: 0;
    transform: translateX(-50%) translateY(16px);
    pointer-events: none;
  }

  .mobile-prompt__icon {
    width: 52px;
    height: 52px;
    display: block;
    opacity: 0.8;
  }

  .mobile-prompt__text {
    margin: 0;
    font-family: "Noto Serif Georgian", Georgia, serif;
    font-size: 13px;
    letter-spacing: 0.03em;
    line-height: 1.65;
    color: var(--text);
  }

  .mobile-prompt__close {
    position: absolute;
    top: 10px;
    right: 12px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 14px;
    color: rgba(176, 85, 117, 0.45);
    padding: 4px 6px;
    line-height: 1;
    transition: color 0.2s ease;
  }

  .mobile-prompt__close:hover {
    color: var(--rose-dark);
  }
}

/* ────────────────────────────────────────
   SITE SHELL (phone column)
──────────────────────────────────────── */
/* ONE background for the whole column - the sections are transparent, so the
   hero → polaroid seam is just a point on a single continuous gradient and
   cannot band. Nothing here may be re-declared on .hero / .shot / .rsvp. */
.site {
  position: relative;
  width: 100%;
  margin: 0 auto;
  background-color: var(--blush);
  background-image: linear-gradient(180deg,
    #fff9f5 0%,
    #ffeff3 13%,
    var(--blush) 32%,
    #ffdde6 51%,
    var(--blush) 71%,
    #fff5f2 100%);
  overflow-x: hidden;
}

/* ────────────────────────────────────────
   PARALLAX DECO
   One layer spanning the whole column, behind every section. The sections
   carry no background of their own, so these show through. Each item gets a
   position, a rotation, a horizontal flip and a --speed the script reads to
   drift it against the scroll. overflow:hidden keeps drifted items from
   adding page height.
──────────────────────────────────────── */
.deco {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.deco__item {
  position: absolute;
  top: var(--top);
  left: var(--left);
  width: var(--size);
  will-change: transform;
}

.deco__item img {
  display: block;
  width: 100%;
  height: auto;
  opacity: var(--op);
  transform: rotate(var(--rot)) scaleX(var(--flip));
}

/* A triangular lattice: --top steps by a constant 4.5% down the page and
   --left cycles through three fixed bands (-6% / 74% / 34%), so every item is
   the same distance from its neighbours. Only the rotation, flip, size and
   drift speed vary - that's what keeps it from reading as a grid. */
.deco__item--1  { --top: 3%;    --left: -6%; --size: 112px; --rot: -12deg; --flip: 1;  --op: 0.5;  --speed: 0.18; }
.deco__item--2  { --top: 7.5%;  --left: 74%; --size: 76px;  --rot: 16deg;  --flip: -1; --op: 0.55; --speed: 0.12; }
.deco__item--3  { --top: 12%;   --left: 34%; --size: 96px;  --rot: 8deg;   --flip: -1; --op: 0.38; --speed: 0.24; }
.deco__item--4  { --top: 16.5%; --left: -6%; --size: 70px;  --rot: -20deg; --flip: 1;  --op: 0.5;  --speed: 0.1; }
.deco__item--5  { --top: 21%;   --left: 74%; --size: 104px; --rot: 22deg;  --flip: -1; --op: 0.45; --speed: 0.2; }
.deco__item--6  { --top: 25.5%; --left: 34%; --size: 80px;  --rot: -10deg; --flip: 1;  --op: 0.42; --speed: 0.14; }
.deco__item--7  { --top: 30%;   --left: -6%; --size: 118px; --rot: 14deg;  --flip: -1; --op: 0.48; --speed: 0.26; }
.deco__item--8  { --top: 34.5%; --left: 74%; --size: 72px;  --rot: -18deg; --flip: 1;  --op: 0.58; --speed: 0.16; }
.deco__item--9  { --top: 39%;   --left: 34%; --size: 92px;  --rot: -6deg;  --flip: 1;  --op: 0.36; --speed: 0.11; }
.deco__item--10 { --top: 43.5%; --left: -6%; --size: 78px;  --rot: 24deg;  --flip: -1; --op: 0.5;  --speed: 0.22; }
.deco__item--11 { --top: 48%;   --left: 74%; --size: 108px; --rot: -16deg; --flip: 1;  --op: 0.46; --speed: 0.13; }
.deco__item--12 { --top: 52.5%; --left: 34%; --size: 74px;  --rot: 12deg;  --flip: -1; --op: 0.4;  --speed: 0.25; }
.deco__item--13 { --top: 57%;   --left: -6%; --size: 114px; --rot: 20deg;  --flip: -1; --op: 0.5;  --speed: 0.15; }
.deco__item--14 { --top: 61.5%; --left: 74%; --size: 82px;  --rot: -22deg; --flip: 1;  --op: 0.54; --speed: 0.19; }
.deco__item--15 { --top: 66%;   --left: 34%; --size: 98px;  --rot: 6deg;   --flip: 1;  --op: 0.37; --speed: 0.1; }
.deco__item--16 { --top: 70.5%; --left: -6%; --size: 68px;  --rot: 18deg;  --flip: -1; --op: 0.5;  --speed: 0.27; }
.deco__item--17 { --top: 75%;   --left: 74%; --size: 106px; --rot: -24deg; --flip: -1; --op: 0.44; --speed: 0.17; }
.deco__item--18 { --top: 79.5%; --left: 34%; --size: 76px;  --rot: 10deg;  --flip: 1;  --op: 0.43; --speed: 0.12; }
.deco__item--19 { --top: 84%;   --left: -6%; --size: 116px; --rot: -8deg;  --flip: 1;  --op: 0.49; --speed: 0.23; }
.deco__item--20 { --top: 88.5%; --left: 74%; --size: 80px;  --rot: 20deg;  --flip: -1; --op: 0.56; --speed: 0.14; }
.deco__item--21 { --top: 93%;   --left: 34%; --size: 100px; --rot: -14deg; --flip: -1; --op: 0.4;  --speed: 0.2; }

/* ────────────────────────────────────────
   HERO
──────────────────────────────────────── */
.hero {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 22px 14px 26px;
  overflow: hidden;
  /* background comes from .site - see the note there */
}

/* floating decorations */
.hero__deco {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

.hero__deco-item {
  position: absolute;
  display: block;
  opacity: 0.85;
}

.hero__deco-item--stars {
  top: 6%;
  left: -4%;
  width: 108px;
  animation: drift 7s ease-in-out infinite;
}

.hero__deco-item--stars2 {
  bottom: 8%;
  right: -6%;
  width: 92px;
  opacity: 0.6;
  transform: rotate(24deg);
  animation: drift 9s ease-in-out infinite 1.4s;
}

.hero__deco-item--sun {
  top: 4%;
  right: 4%;
  width: 62px;
  animation: spin-slow 26s linear infinite;
}

@keyframes drift {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-14px) rotate(-6deg); }
}

@keyframes spin-slow {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* head */
.hero__head {
  position: relative;
  z-index: 2;
  text-align: center;
}

.hero__names {
  margin: 0;
  font-size: 44px;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0.02em;
  color: var(--rose-dark);
}

.hero__amp {
  font-weight: 400;
  font-size: 24px;
  color: var(--gold-mid);
  padding: 0 2px;
}

/* english line - real latin text, so it must NOT use Aladini.
   Quiet rounded sans: matches the soft 3d machine and leaves the calligraphy
   as the only ornate voice. lining-nums keeps the "1" a real 1, not an oldstyle
   figure that reads as "I". */
.hero__eyebrow {
  margin: 8px 0 0;
  font-family: "Quicksand", "Segoe UI", sans-serif;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  font-variant-numeric: lining-nums;
  color: var(--text-soft);
}

/* ────────────────────────────────────────
   STAGE + MACHINE
──────────────────────────────────────── */
.stage {
  position: relative;
  z-index: 2;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* clearance so the knob is not clipped at the peak of the swing */
  /* padding-right: 28px; */
  animation: float 6s ease-in-out infinite;
}

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

.machine {
  position: relative;
  flex: 0 0 auto;
  cursor: pointer;
  outline: none;
  -webkit-user-select: none;
  user-select: none;
  transition: transform 0.18s ease;
}

.machine:active {
  transform: scale(0.985);
}

.machine:focus-visible {
  outline: 2px dashed var(--pink-deep);
  outline-offset: 10px;
  border-radius: 20px;
}

.machine__img {
  display: block;
  width: auto;
  /* --mh is the number the .vh-lock rule multiplies --vh by; the two lines
     under it are the JS-off fallback. Change the number in both places. */
  --mh: 56;
  height: 56vh;
  height: 56svh;
  min-height: 320px;
  max-height: 484px;
  max-width: 320px;
  position: relative;
  z-index: 2;
  /* filter: drop-shadow(0 16px 26px rgba(176, 85, 117, 0.24)); */
  pointer-events: none;
}

.machine.is-shake {
  animation: shake 0.62s ease-in-out;
}

@keyframes shake {
  0%   { transform: translateX(0) rotate(0deg); }
  15%  { transform: translateX(-5px) rotate(-1.1deg); }
  30%  { transform: translateX(5px) rotate(1.1deg); }
  45%  { transform: translateX(-4px) rotate(-0.8deg); }
  60%  { transform: translateX(4px) rotate(0.8deg); }
  78%  { transform: translateX(-2px) rotate(-0.3deg); }
  100% { transform: translateX(0) rotate(0deg); }
}

/* heart lamps on the top bar */
.lamps {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.lamp {
  position: absolute;
  top: 24%;
  width: 9%;
  height: 4.6%;
  margin-top: -2.3%;
  margin-left: -4.5%;
  border-radius: 50%;
  background-image: radial-gradient(circle, rgba(255, 236, 150, 0.95) 0%, rgba(255, 180, 205, 0.55) 45%, rgba(255, 180, 205, 0) 72%);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.lamp:nth-child(1) { left: 35.4%; }
.lamp:nth-child(2) { left: 44.8%; }
.lamp:nth-child(3) { left: 54.5%; }
.lamp:nth-child(4) { left: 63.6%; }

.machine.is-live .lamp {
  animation: lamp-blink 0.62s ease-in-out infinite;
}

.machine.is-live .lamp:nth-child(1) { animation-delay: 0s; }
.machine.is-live .lamp:nth-child(2) { animation-delay: 0.15s; }
.machine.is-live .lamp:nth-child(3) { animation-delay: 0.3s; }
.machine.is-live .lamp:nth-child(4) { animation-delay: 0.45s; }

@keyframes lamp-blink {
  0%, 100% { opacity: 0.08; }
  50%      { opacity: 1; }
}

.machine.is-won .lamp {
  animation: lamp-party 0.42s ease-in-out 6;
}

.machine.is-won .lamp:nth-child(1) { animation-delay: 0s; }
.machine.is-won .lamp:nth-child(2) { animation-delay: 0.1s; }
.machine.is-won .lamp:nth-child(3) { animation-delay: 0.2s; }
.machine.is-won .lamp:nth-child(4) { animation-delay: 0.3s; }

@keyframes lamp-party {
  0%, 100% { opacity: 0; }
  50%      { opacity: 1; }
}

/* ────────────────────────────────────────
   SCREEN - sits exactly over the machine display
──────────────────────────────────────── */
.screen {
  position: absolute;
  left: 26.8%;
  top: 30.6%;
  width: 46.9%;
  height: 13.5%;
  z-index: 3;
  overflow: hidden;
  border-radius: 9px;
  background-color: var(--screen);
  box-shadow:
    inset 0 3px 7px rgba(176, 85, 117, 0.16),
    inset 0 -2px 5px rgba(255, 255, 255, 0.7);
  transition: background-color 0.4s ease, box-shadow 0.4s ease;
}

.screen.is-win {
  background-color: var(--screen-lit);
  box-shadow:
    inset 0 3px 7px rgba(246, 199, 157, 0.4),
    0 0 0 2px rgba(255, 210, 63, 0.35),
    0 0 22px rgba(255, 195, 120, 0.55);
}

.screen__reels {
  position: relative;
  display: flex;
  align-items: stretch;
  width: 100%;
  height: 100%;
}

.reel {
  position: relative;
  flex: 1 1 0;
  height: 100%;
  overflow: hidden;
}

.reel__divider {
  flex: 0 0 1px;
  align-self: center;
  height: 62%;
  background-color: rgba(180, 112, 92, 0.22);
}

.reel__strip {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  will-change: transform;
}

.reel__item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--rose-dark);
  transition: color 0.35s ease, text-shadow 0.35s ease;
}

.reel__item span {
  white-space: nowrap;
}

/* the resting heart, trimmed a touch so the emoji's own padding doesn't make
   it read larger than the digits it sits between */
.reel__item--heart span {
  font-size: 0.86em;
  line-height: 1;
}

.screen.is-win .reel__item {
  color: #c0517a;
  text-shadow: 0 0 12px rgba(255, 190, 110, 0.75);
}

.screen__shine {
  position: absolute;
  top: 0;
  left: -40%;
  width: 34%;
  height: 100%;
  background-image: linear-gradient(105deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.75) 50%, rgba(255, 255, 255, 0) 100%);
  transform: skewX(-14deg);
  pointer-events: none;
}

.screen.is-win .screen__shine {
  animation: sweep 0.9s ease-out 1;
}

@keyframes sweep {
  from { left: -40%; }
  to   { left: 118%; }
}

/* ────────────────────────────────────────
   HANDLE
──────────────────────────────────────── */
.handle {
  position: absolute;
  right: -12.4%;
  top: 26%;
  width: 28%;
  z-index: 1;
  pointer-events: none;
}

/* the lever is drawn left-handed, then mirrored onto the right side -
   this also flips the arm's rotation, so the knob swings out to the right */
.handle__svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  transform: scaleX(-1);
  /* filter: drop-shadow(0 5px 8px rgba(176, 85, 117, 0.28)); */
}

.handle__arm {
  transform-box: view-box;
  transform-origin: 112px 230px;
  transform: rotate(-20deg);
  transition: transform 0.78s cubic-bezier(0.34, 1.42, 0.5, 1);
}

.handle.is-pulling .handle__arm {
  transform: rotate(-72deg);
  transition: transform 0.34s cubic-bezier(0.45, 0.05, 0.55, 0.95);
}

.machine:hover .handle__arm {
  transform: rotate(-25deg);
}

.machine:hover .handle.is-pulling .handle__arm {
  transform: rotate(-72deg);
}

/* ────────────────────────────────────────
   WIN BURST
──────────────────────────────────────── */
.burst {
  position: absolute;
  left: 50%;
  top: 37%;
  width: 0;
  height: 0;
  z-index: 5;
  pointer-events: none;
}

.spark {
  position: absolute;
  left: 0;
  top: 0;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 2px;
  opacity: 0;
  animation: spark-fly 1.05s ease-out forwards;
}

@keyframes spark-fly {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.3) rotate(0deg);
  }
  18% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(var(--tx), var(--ty)) scale(1) rotate(var(--rot));
  }
}

/* ────────────────────────────────────────
   CAPTION (hint → result)
──────────────────────────────────────── */
.caption {
  position: relative;
  z-index: 2;
  width: 100%;
  min-height: 76px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.caption__hint {
  position: absolute;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  font-size: 17px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--text-soft);
  transition: opacity 0.45s ease, transform 0.45s ease;
}

/* real english word - must NOT inherit Aladini, whose cmap turns latin
   letters into georgian glyphs ("SPIN" comes out as შ-ჶ-ჟ-N) */
.caption__hint-text {
  font-family: "Quicksand", "Segoe UI", sans-serif;
  font-size: 18px;
  font-weight: 900;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  text-transform: uppercase;
}

.caption__hint.is-out {
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
}

/* points up at the machine - with rotate(135deg) the local translate below
   resolves to straight up, so the chevron nods toward what to press */
.caption__arrow {
  display: block;
  width: 11px;
  height: 11px;
  border-left: 2px solid var(--pink);
  border-bottom: 2px solid var(--pink);
  transform: rotate(135deg);
  animation: nudge 1.6s ease-in-out infinite;
}

@keyframes nudge {
  0%, 100% { transform: rotate(135deg) translate(0, 0); opacity: 0.5; }
  50%      { transform: rotate(135deg) translate(-4px, 4px); opacity: 1; }
}

.caption__result {
  position: absolute;
  opacity: 0;
  transform: translateY(14px) scale(0.94);
  pointer-events: none;
  transition: opacity 0.6s ease 0.15s, transform 0.6s cubic-bezier(0.34, 1.4, 0.6, 1) 0.15s;
}

.caption__result.is-in {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.caption__date {
  margin: 0;
  font-size: 27px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--rose-dark);
}

.caption__note {
  margin: 2px 0 0;
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0.12em;
  color: var(--text-soft);
}

/* ────────────────────────────────────────
   POLAROID SHOT
   The print lives BEHIND the camera (z-index 1 vs 2) and is tucked fully
   inside its opaque body, so no mask is needed - it slides down out of the
   bottom edge on .is-shot.
──────────────────────────────────────── */
.shot {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 40px 14px 44px;
  overflow: hidden;
  /* background comes from .site - see the note there */
}

.shot__title {
  margin: 0;
  font-size: 30px;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0.02em;
  text-align: center;
  color: var(--rose-dark);
}

/* the venue doubles as the map link */
.shot__place {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: inherit;
  text-decoration: none;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.shot__place span {
  font-size: 40px;
}

.shot__place:hover {
  opacity: 0.78;
  transform: translateY(-2px);
}

.shot__place:focus-visible {
  outline: 2px dashed var(--pink-deep);
  outline-offset: 8px;
  border-radius: 12px;
}

.shot__pin {
  display: block;
  width: 35px;
  height: auto;
  /* the glyphs sit high in this face - nudges the pin onto the text baseline */
  margin-bottom: 5px;
  /* filter: drop-shadow(0 3px 5px rgba(176, 85, 117, 0.24)); */
}

/* ── map card ─────────────────────────
   The <a> is the whole card. The iframe inside it is deliberately inert -
   pointer-events:none hands every tap straight through to the link, so the
   guest opens the location in a new tab instead of dragging the embed around
   inside a 190px window (which on a phone just traps the scroll). */
.shot__map {
  position: relative;
  display: block;
  width: 100%;
  max-width: 320px;
  height: 190px;
  margin-top: -6px;
  border: 3px solid #ffffff;
  border-radius: 22px;
  overflow: hidden;
  box-shadow:
    0 0 0 1px var(--blush-deep),
    0 14px 26px rgba(176, 85, 117, 0.2);
  transition: transform 0.35s cubic-bezier(0.34, 1.3, 0.64, 1), box-shadow 0.35s ease;
}

.shot__map:hover,
.shot__map:focus-visible {
  transform: translateY(-3px);
  box-shadow:
    0 0 0 1px var(--pink),
    0 18px 32px rgba(176, 85, 117, 0.28);
}

.shot__map:focus-visible {
  outline: 2px dashed var(--pink-deep);
  outline-offset: 6px;
}

.shot__map-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  pointer-events: none;
  /* google's default green/grey is loud next to the blush column - pulling
     the saturation down and warming it lets the map sit in the palette */
  filter: saturate(0.62) contrast(0.94) brightness(1.04) sepia(0.12);
}

/* warm wash over the tiles, heaviest at the foot where the chip sits */
.shot__map-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(180deg,
    rgba(255, 233, 238, 0.32) 0%,
    rgba(255, 233, 238, 0.08) 34%,
    rgba(176, 85, 117, 0.16) 78%,
    rgba(176, 85, 117, 0.34) 100%);
}

.shot__map-chip {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 15px 8px;
  font-size: 17px;
  line-height: 1;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: var(--rose-dark);
  background-color: rgba(255, 255, 255, 0.94);
  border-radius: 999px;
  box-shadow: 0 4px 12px rgba(176, 85, 117, 0.26);
  transition: background-color 0.3s ease;
}

.shot__map:hover .shot__map-chip {
  background-color: #ffffff;
}

.shot__map-chip-pin {
  display: block;
  width: 17px;
  height: auto;
  margin-bottom: 2px;
}

.shot__couple {
  display: block;
  width: 96px;
  height: auto;
  animation: float 6s ease-in-out infinite;
}

/* the padding is the print's landing strip - the photo overflows the polaroid
   box by ~55% of the camera's height, and the venue line sits under that */
.shot__stage {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-bottom: 176px;
  margin-top: -2.5rem;
}

.polaroid {
  position: relative;
  flex: 0 0 auto;
  width: 74vw;
  max-width: 280px;
}

.polaroid__cam {
  position: relative;
  z-index: 2;
  display: block;
  width: 100%;
  height: auto;
  /* filter: drop-shadow(0 14px 24px rgba(176, 85, 117, 0.24)); */
}

.polaroid__print {
  position: absolute;
  z-index: 1;
  left: 50%;
  /* the png carries ~4.5% transparent padding under the camera - the print
     has to sit above that or a sliver of it shows before the shutter fires */
  bottom: 6%;
  width: 50%;
  transform: translate(-50%, 0) rotate(0deg);
  transition: transform 2.6s cubic-bezier(0.16, 1, 0.3, 1) 0.42s;
  /* filter: drop-shadow(0 8px 14px rgba(176, 85, 117, 0.26)); */
}

/* 105% of the print's own height clears the camera's bottom lip entirely,
   so the whole photo ends up in view, hanging just under the slot */
.polaroid.is-shot .polaroid__print {
  transform: translate(-50%, 105%) rotate(-1.6deg);
}

.polaroid__photo {
  display: block;
  width: 100%;
  height: auto;
}

/* the print develops as it clears the slot */
.polaroid__develop {
  position: absolute;
  inset: 0;
  background-color: #fdf6f2;
  opacity: 1;
  pointer-events: none;
  transition: opacity 1.9s ease 0.95s;
}

.polaroid.is-shot .polaroid__develop {
  opacity: 0;
}

/* bloom around the camera itself */
.polaroid__flash {
  position: absolute;
  z-index: 3;
  inset: -55%;
  border-radius: 50%;
  opacity: 0;
  transform: scale(0.72);
  pointer-events: none;
  background-image: radial-gradient(circle at 50% 44%,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 255, 255, 0.96) 18%,
    rgba(255, 246, 242, 0.7) 38%,
    rgba(255, 226, 234, 0) 70%);
}

/* and a hard white pop across the whole viewport - this is what sells it.
   Fixed and outside .site so nothing clips it and it covers the page, not
   just the polaroid section. */
.flash {
  position: fixed;
  z-index: 9998;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  background-color: #ffffff;
}

.flash.is-firing {
  animation: room-flash 0.62s ease-out both;
}

.polaroid.is-shot .polaroid__flash {
  animation: cam-flash 0.9s ease-out both;
}

.polaroid.is-shot .polaroid__cam {
  animation: cam-kick 0.62s ease-out;
}

@keyframes cam-flash {
  0%   { opacity: 0;    transform: scale(0.72); }
  5%   { opacity: 1;    transform: scale(1.06); }
  16%  { opacity: 0.62; transform: scale(1); }
  34%  { opacity: 0.28; transform: scale(1.02); }
  100% { opacity: 0;    transform: scale(1.04); }
}

@keyframes room-flash {
  0%   { opacity: 0; }
  4%   { opacity: 0.92; }
  14%  { opacity: 0.5; }
  38%  { opacity: 0.14; }
  100% { opacity: 0; }
}

@keyframes cam-kick {
  0%   { transform: scale(1) rotate(0deg); }
  14%  { transform: scale(1.035) rotate(-1deg); }
  42%  { transform: scale(0.992) rotate(0.4deg); }
  100% { transform: scale(1) rotate(0deg); }
}

/* ────────────────────────────────────────
   TIMETABLE
   A rail with a dot per step, each dot lined up with the time inside its card.
   The rail is stitched from one segment per row (see .plan__step::before) so
   it always begins and ends on a dot, and the rows fade in one after the other
   on scroll - the script adds .is-in, the --i in the markup staggers them.
──────────────────────────────────────── */
.plan {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 30px;
  padding: 56px 18px 60px;
  /* the vertical gap between rows. The rail segments reach across it, so the
     two values must stay equal - hence one source. */
  --row-gap: 16px;
  /* background comes from .site - see the note there */
}

.plan__head {
  text-align: center;
}

.plan__title {
  margin: 0;
  font-size: 32px;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0.02em;
  color: var(--rose-dark);
}

.plan__rule {
  display: block;
  width: 104px;
  height: 2px;
  margin: 13px auto 0;
  border-radius: 2px;
  background-image: linear-gradient(90deg,
    rgba(246, 199, 157, 0) 0%,
    var(--gold) 28%,
    var(--pink) 50%,
    var(--gold) 72%,
    rgba(246, 199, 157, 0) 100%);
}

.plan__list {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: var(--row-gap);
}

.plan__step {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  gap: 16px;
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.34, 1.25, 0.64, 1);
  /* --i is set per step in the markup - the rows land one after the other
     rather than all at once */
  transition-delay: calc(var(--i, 0) * 95ms);
}

.plan__step.is-in {
  opacity: 1;
  transform: translateY(0);
}

/* the rail, drawn one segment at a time: each step carries the line from its
   own dot down across the row gap to the next dot, and the last step draws
   nothing. A single line spanning the whole list would have to guess where the
   final dot sits, and cards are not all the same height - this way the thread
   always ends exactly on it, however the text wraps. */
.plan__step::before {
  content: '';
  position: absolute;
  z-index: 0;
  left: 8px;
  top: 26px;                                /* the dot's centre - see below */
  bottom: calc(var(--row-gap) * -1);        /* meets the next segment, no seam */
  width: 2px;
  border-radius: 2px;
  background-image: linear-gradient(180deg, var(--pink) 0%, var(--gold) 100%);
  opacity: 0.7;
}

.plan__step:last-child::before {
  display: none;
}

/* 18px wide starting at the row's left edge → its centre lands on x=9px,
   which is the centre of the 2px rail at left:8px. margin-top puts that centre
   at y=26px, on the time line inside the card - and that is the 26px the rail
   segment above starts from. z-index lifts it over the rail's rounded tip. */
.plan__dot {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-top: 17px;
  border-radius: 50%;
  background-color: var(--pink-deep);
  border: 3px solid var(--cream);
  box-shadow:
    0 0 0 2px var(--blush-deep),
    0 3px 8px rgba(176, 85, 117, 0.3);
}

.plan__card {
  flex: 1 1 auto;
  min-width: 0;
  padding: 13px 18px 15px;
  background-color: rgba(255, 255, 255, 0.74);
  border: 2px solid rgba(255, 255, 255, 0.9);
  border-radius: 20px;
  box-shadow: 0 8px 20px rgba(176, 85, 117, 0.14);
  transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease;
}

.plan__card:hover {
  transform: translateY(-2px);
  background-color: rgba(255, 255, 255, 0.9);
  box-shadow: 0 12px 26px rgba(176, 85, 117, 0.2);
}

/* real digits - Aladini's cmap is latin-only and its figures read as
   calligraphy next to the clock, so the time gets the same quiet sans the
   hero eyebrow uses. tabular-nums keeps every row's colon on one axis. */
.plan__time {
  margin: 0;
  font-family: "Quicksand", "Segoe UI", sans-serif;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--gold-deep);
}

.plan__name {
  margin: 3px 0 0;
  font-size: 25px;
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: 0.02em;
  color: var(--rose-dark);
}

.plan__place {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 4px 0 0;
  font-size: 16px;
  line-height: 1.35;
  color: var(--text-soft);
}

/* a small bead in place of a pin icon - at this size an image would only
   muddy, and the bead echoes the rail's dots */
.plan__place::before {
  content: '';
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: var(--gold);
}

/* ────────────────────────────────────────
   RSVP
──────────────────────────────────────── */
.rsvp {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 46px 16px 40px;
  /* background comes from .site - see the note there */
}

/* full-bleed band at the foot of the section - the negative margins cancel
   the section's side padding and sit it flush on the bottom edge */
.rsvp__dance {
  display: block;
  width: calc(100% + 32px);
  height: auto;
  margin: 4px -16px -40px;
  /* pinned at the floor, so the sway reads as weight on the feet */
  transform-origin: 50% 100%;
  animation: dance-sway 6s ease-in-out infinite;
}

/* deliberately uneven keyframes - evenly spaced ones read as a metronome.
   The drift stays inside the 16px side bleed so no edge is ever exposed. */
@keyframes dance-sway {
  0%, 100% { transform: translate3d(-3px, 0, 0) rotate(-0.7deg); }
  30%      { transform: translate3d(0, -6px, 0) rotate(0.5deg); }
  55%      { transform: translate3d(3px, -2px, 0) rotate(0.9deg); }
  80%      { transform: translate3d(1px, -5px, 0) rotate(-0.2deg); }
}

.rsvp__title {
  margin: 0;
  font-size: 32px;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0.02em;
  text-align: center;
  color: var(--rose-dark);
}

.rsvp__form {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  width: 100%;
  max-width: 320px;
}

/* Guests type their own name here, so this is the one field that must NOT use
   Aladini: its latin cmap would turn a name typed in latin into georgian
   calligraphy. Plain Noto renders whatever they actually typed. */
.rsvp__input {
  width: 100%;
  padding: 13px 18px;
  font-family: "Noto Serif Georgian", Georgia, serif;
  font-size: 17px;
  color: var(--rose-dark);
  text-align: center;
  background-color: rgba(255, 255, 255, 0.72);
  border: 2px solid var(--blush-deep);
  border-radius: 999px;
  outline: none;
  transition: border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease;
}

.rsvp__input::placeholder {
  color: var(--text-soft);
  opacity: 0.75;
}

.rsvp__input:focus {
  border-color: var(--pink);
  background-color: #ffffff;
  box-shadow: 0 0 0 4px rgba(245, 168, 189, 0.22);
}

.rsvp__buttons {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: 100%;
}

.rsvp__btn {
  flex: 1 1 0;
  padding: 14px 10px 16px;
  font-family: "Aladini", "Noto Serif Georgian", Georgia, serif;
  font-size: 24px;
  line-height: 1;
  color: #ffffff;
  border: none;
  border-radius: 22px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
}

.rsvp__btn--yes {
  background-color: var(--pink-deep);
  box-shadow: 0 6px 0 #b4536e, 0 10px 18px rgba(176, 85, 117, 0.28);
}

.rsvp__btn--no {
  background-color: var(--gold);
  color: var(--gold-deep);
  box-shadow: 0 6px 0 #dcae86, 0 10px 18px rgba(176, 85, 117, 0.2);
}

.rsvp__btn:hover {
  filter: brightness(1.04);
  transform: translateY(-2px);
}

/* the button squashes onto its own shadow */
.rsvp__btn:active {
  transform: translateY(5px);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12);
}

.rsvp__btn:focus-visible {
  outline: 2px dashed var(--pink-deep);
  outline-offset: 5px;
}

/* ── answered state ──────────────────────
   One answer per device: once it is in, the controls lock so a second tap -
   or a reload - cannot send a different one. */
.rsvp__done {
  display: none;
  margin: 0;
  font-size: 17px;
  letter-spacing: 0.03em;
  text-align: center;
  color: var(--text-soft);
}

/* validation nudge / in-flight / send failure - collapses when there is
   nothing to say, so it does not open a gap in the form's flex gap */
.rsvp__status {
  margin: 0;
  font-size: 16px;
  letter-spacing: 0.03em;
  text-align: center;
  color: var(--text-soft);
}

.rsvp__status:empty {
  display: none;
}

.rsvp__status.is-error {
  color: var(--pink-deep);
}

.rsvp.is-answered .rsvp__done {
  display: block;
}

.rsvp__btn[disabled] {
  cursor: default;
  opacity: 0.4;
  transform: none;
  box-shadow: none;
  filter: none;
}

/* the answer that was actually given stays legible */
.rsvp__btn[disabled].is-chosen {
  opacity: 1;
}

.rsvp__input[disabled] {
  opacity: 0.55;
  background-color: rgba(255, 255, 255, 0.5);
}

/* ────────────────────────────────────────
   ANSWER MODAL
   https://weddsite.b-cdn.net/showcase/pro-9/card.png is the frame; everything sits inside its printed border, so the
   box keeps the art's 1:1 ratio and pads in by the border's thickness.
──────────────────────────────────────── */
.modal {
  position: fixed;
  z-index: 9997;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background-color: rgba(106, 73, 87, 0);
  visibility: hidden;
  transition: background-color 0.4s ease, visibility 0s linear 0.4s;
}

.modal.is-open {
  visibility: visible;
  background-color: rgba(106, 73, 87, 0.42);
  transition: background-color 0.4s ease, visibility 0s;
}

.modal__box {
  position: relative;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* clears the printed frame and its flower corners */
  padding: 19% 16%;
  text-align: center;
  background-image: url("https://weddsite.b-cdn.net/showcase/pro-9/card.png");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  opacity: 0;
  transform: scale(0.82) rotate(-3deg);
  transition: opacity 0.35s ease, transform 0.45s cubic-bezier(0.34, 1.5, 0.5, 1);
}

.modal.is-open .modal__box {
  opacity: 1;
  transform: scale(1) rotate(0deg);
}

.modal__sun {
  display: block;
  width: 82px;
  height: auto;
  animation: sun-bob 3.4s ease-in-out infinite;
}

@keyframes sun-bob {
  0%, 100% { transform: translateY(0) rotate(-4deg); }
  50%      { transform: translateY(-7px) rotate(4deg); }
}

.modal__head {
  margin: 0;
  font-size: 21px;
  line-height: 1.35;
  color: var(--rose-dark);
}

.modal__body {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
}

.modal__close {
  position: absolute;
  /* inside the printed border, clear of the corner flowers */
  top: 13.5%;
  right: 13.5%;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Quicksand", "Segoe UI", sans-serif;
  font-size: 13px;
  line-height: 1;
  color: var(--rose-dark);
  background-color: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--blush-deep);
  border-radius: 50%;
  cursor: pointer;
  transition: color 0.2s ease, background-color 0.2s ease;
}

.modal__close:hover {
  color: #ffffff;
  background-color: var(--pink-deep);
}

/* the page must not scroll behind an open modal */
body.is-locked {
  overflow: hidden;
}

/* ────────────────────────────────────────
   REDUCED MOTION
──────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .stage,
  .hero__deco-item,
  .caption__arrow,
  .shot__couple,
  .rsvp__dance,
  .modal__sun {
    animation: none;
  }

  /* the print still ends up out of the slot - it just gets there at once */
  .polaroid__print,
  .polaroid__develop {
    transition: none;
  }

  .polaroid.is-shot .polaroid__flash,
  .polaroid.is-shot .polaroid__cam,
  .flash.is-firing {
    animation: none;
  }

  /* the timetable rows are already marked in by the script - this just stops
     them sliding on the way */
  .plan__step {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ────────────────────────────────────────
   481px +
──────────────────────────────────────── */
@media (min-width: 581px) {
  .hero {
    gap: 14px;
    padding: 30px 18px 34px;
  }

  .hero__eyebrow {
    font-size: 17px;
  }

  .machine__img {
    --mh: 58;
    height: 58vh;
    height: 58svh;
    max-height: 520px;
    max-width: 346px;
  }

  .hero__deco-item--stars {
    width: 124px;
  }

  .hero__deco-item--sun {
    width: 74px;
  }

  .caption__hint {
    font-size: 18px;
  }

  .caption__date {
    font-size: 30px;
  }

  .shot {
    gap: 30px;
    padding: 44px 18px 48px;
  }

  .shot__title {
    font-size: 33px;
  }

  .shot__pin {
    width: 33px;
  }

  .shot__couple {
    width: 110px;
  }

  .shot__stage {
    padding-bottom: 200px;
  }

  .polaroid {
    max-width: 320px;
  }

  .shot__map {
    max-width: 360px;
    height: 214px;
  }

  .shot__map-chip {
    font-size: 18px;
  }

  .plan {
    gap: 34px;
    padding: 60px 22px 64px;
    --row-gap: 18px;
  }

  .plan__title {
    font-size: 35px;
  }

  .plan__list {
    max-width: 380px;
  }

  .plan__card {
    padding: 15px 20px 17px;
  }

  .plan__time {
    font-size: 15px;
  }

  .plan__name {
    font-size: 27px;
  }

  .plan__place {
    font-size: 17px;
  }

  .rsvp__title {
    font-size: 35px;
  }

  .modal__sun {
    width: 94px;
  }

  .modal__head {
    font-size: 23px;
  }

  .modal__body {
    font-size: 16px;
  }
}

/* ────────────────────────────────────────
   769px + - keep the phone column, show notice
──────────────────────────────────────── */
@media (min-width: 769px) {
  body {
    background-color: #fbe2e8;
  }

  .site {
    max-width: 460px;
    min-height: 100vh;
    min-height: 100svh;
    box-shadow: 0 0 60px rgba(176, 85, 117, 0.16);
  }

  .vh-lock .site {
    min-height: calc(var(--vh) * 100);
  }

  .machine__img {
    --mh: 60;
    height: 60vh;
    height: 60svh;
    max-height: 546px;
    max-width: 364px;
  }

  .shot__couple {
    width: 118px;
  }

  .shot__stage {
    padding-bottom: 212px;
  }

  .polaroid {
    max-width: 340px;
  }

  .shot__map {
    max-width: 380px;
    height: 226px;
  }

  .plan__list {
    max-width: 400px;
  }

  /* the column is narrow here, so the deco grows a little to keep filling it */
  .deco__item {
    width: calc(var(--size) * 1.15);
  }
}

/* ────────────────────────────────────────
   1025px +
──────────────────────────────────────── */
@media (min-width: 1025px) {
  .site {
    max-width: 480px;
  }
}


/* ===========================================================================
   Loading screen
   ---------------------------------------------------------------------------
   Shared across every Weddsite template; only the palette below is per design.
   The viewport pin this template needs already lives at the top of this file
   (--vh, the 100vh/100svh floors and the .vh-lock overrides) - do not restate it.

   The loader exists so `font-display: block` is safe: the block period is spent
   behind the spinner instead of on an invisible page. It also holds the entry
   animation (via .is-loading) so the intro is not played out behind it.
   =========================================================================== */

/* Entry animations must not play out behind the spinner - a guest who uncovers a
   finished, static scene has been shown nothing. `is-loading` is set by the
   inline script in <head> before first paint and removed on reveal, so a paused
   animation resumes from frame zero exactly when the guest can first see it.
   The spinner itself is excluded, and with JS off the class never lands. */
.is-loading body *:not(.loader):not(.loader__spinner) {
    animation-play-state: paused !important;
}

.loader {
    position: fixed;
    inset: 0;
    /* Out-ranks every entry overlay (envelopes / curtains / clouds live at 9999+). */
    z-index: 100000;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #fff7f3;
    opacity: 1;
    transition: opacity 0.55s ease;
}

.loader.is-hidden {
    opacity: 0;
}

.loader.is-gone {
    display: none;
}

.loader__spinner {
    display: block;
    width: 40px;
    height: 40px;
    border: 2px solid #ffd9e2;
    border-top-color: #d9718f;
    border-radius: 50%;
    animation: loader-spin 0.9s linear infinite;
}

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