/* ==========================================================================
   STYLE.CSS  —  you should not need to open this file for a new client.

   IMPORTANT: there are no colour codes written in here on purpose.
   Every colour is read from js/config.js and pushed into the page by
   JavaScript. Change the palette in config.js and the whole site
   changes with it.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. DESIGN TOKENS
   Small reusable values for spacing, corners and timing, so the look
   stays consistent everywhere. (Colours are injected from config.js.)
   -------------------------------------------------------------------------- */
:root {
  /* spacing scale */
  --space-1: 0.375rem;
  --space-2: 0.75rem;
  --space-3: 1.125rem;
  --space-4: 1.75rem;
  --space-5: 2.5rem;
  --space-6: 4rem;

  /* rounded corners */
  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-pill: 999px;

  /* height of the top bar — a slim strip, not a header block.
     Sections use this so headings never hide behind it. */
  --nav-h: 52px;

  /* THE FADE ZONE.
     Every section carries this much empty padding at its top and bottom,
     and its background picture dissolves across exactly that distance.
     So each section fades out inside its OWN footprint, and neighbours
     meet at a boundary where both are already plain cream. Nothing is
     ever pulled up into the section above it. */
  --fade-pad: clamp(140px, 22vw, 180px);
  --fade-ramp: 210px;   /* longer, now that no blur softens the join */

  /* the soft easing curve used by all reveal animations */
  --ease-soft: cubic-bezier(0.22, 1, 0.36, 1);

  /* how long things take */
  --t-fast: 200ms;
  --t-nav: 350ms;
  --t-menu: 400ms;
  --t-reveal: 700ms;
}

@media (min-width: 900px) {
  :root { --nav-h: 60px; }
}


/* --------------------------------------------------------------------------
   2. RESET & PAGE BASICS
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* "clip" instead of "hidden": it still stops any sideways scrollbar,
     but it does NOT turn the page into a scroll container, which is what
     silently breaks position:sticky on iPhones. Nothing above the pinned
     section may use overflow:hidden. */
  overflow-x: clip;
}

body {
  margin: 0;
  background-color: var(--color-bg);
  color: var(--color-ink);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}

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

/* The one place the saree pink appears on its own — highlighting text. */
::selection { background-color: var(--color-blossomTint); }

a { color: inherit; text-decoration: none; }

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

/* Sections start below the fixed bar when you jump to them */
section { scroll-margin-top: var(--nav-h); }

/* The empty sections that later stages will fill.
   They hold a little space so scrolling still feels natural today.
   content-visibility tells the browser not to bother laying them out
   while they are off screen, which keeps scrolling cheap. */
/* (all sections are built now — nothing left to defer) */


/* --------------------------------------------------------------------------
   3. TYPOGRAPHY
   The whole page is English and uses the Latin faces. Only two blocks
   ever show Marathi, and they mark themselves element by element.
   -------------------------------------------------------------------------- */

/* The page is English. Latin faces are the default everywhere. */
body {
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
}

/* The display face, used for the big confident lines. */
.display,
.hero__family,
.hero__occasion,
.menu__link,
.place__venue,
.countdown__done,
.mapCard__fallbackName,
h1, h2, h3 {
  font-family: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  font-weight: 600;
}

/* ---- The only Marathi on the page ----
   Just two blocks ever switch language: the section 2 message and the
   section 3 timeline. When they do, JavaScript puts .is-marathi on those
   individual elements — nothing else on the page is touched. */
.is-marathi {
  font-family: "Tiro Devanagari Marathi", "Noto Serif Devanagari", system-ui, serif;
  font-weight: 400;
  overflow-wrap: break-word;
}

/* ---- THE TWO FACES OF THE LETTER, THE TIMELINE AND THE SIGNOFF ----
   These three areas — and only these three — are set in Playfair Display
   for English and Tiro Devanagari Marathi for Marathi. Everything else
   the faces it already had: the hero, "Memories", the Location headings,
   the countdown, the nav and the footer are untouched.

   The shloka is the one exception inside this scope. It keeps Noto Serif
   Devanagari, set further down, because it is Sanskrit scripture rather
   than part of the letter's voice. */
.letter__subheading,
.letter__para,
.letter__closing,
.stop__title,
.stop__date,
.stop__time,
.signoff__thanks,
.signoff__family {
  font-family: "Playfair Display", Georgia, "Times New Roman", serif;
}

.letter__subheading,
.letter__closing,
.stop__title,
.signoff__family {
  font-weight: 600;
}

.letter__para,
.stop__date,
.stop__time,
.signoff__thanks {
  font-weight: 400;
}

/* the same areas, in Marathi */
.letter__subheading.is-marathi,
.letter__para.is-marathi,
.letter__closing.is-marathi,
.stop__title.is-marathi,
.stop__date.is-marathi,
.stop__time.is-marathi,
.signoff__thanks.is-marathi,
.signoff__family.is-marathi {
  font-family: "Tiro Devanagari Marathi", "Noto Serif Devanagari", system-ui, serif;
}

/* TIRO HAS ONE WEIGHT ONLY. The lines that are bold in English stay at
   400 in Marathi, because a browser asked for a bold it does not have
   smears the letterforms — the same reason the shloka was moved off
   this face. Devanagari carries its emphasis through size, not weight. */
.letter__subheading.is-marathi,
.letter__closing.is-marathi,
.stop__title.is-marathi,
.signoff__family.is-marathi {
  font-weight: 400;
}

/* Type scale. clamp() means: never smaller than the first value,
   never bigger than the last, and it flows smoothly in between. */
h1 { margin: 0; }

body { font-size: clamp(1rem, 0.96rem + 0.3vw, 1.0625rem); line-height: 1.65; }


/* --------------------------------------------------------------------------
   4. LANGUAGE CROSS-FADE
   When the guest switches language, the words fade out, swap, fade in.
   -------------------------------------------------------------------------- */
[data-text] {
  transition: opacity var(--t-fast) ease;
}
body.is-swapping [data-text] { opacity: 0; }


/* --------------------------------------------------------------------------
   5. SCROLL-REVEAL ENGINE
   Elements marked data-reveal start invisible and slide gently into place
   when they scroll into view. This engine is reused by every later section.

   The ".js" class is added by JavaScript. That means if JavaScript ever
   fails to run, nothing is hidden and all content stays visible.
   -------------------------------------------------------------------------- */
.js [data-reveal] {
  opacity: 0;
  transition:
    opacity var(--t-reveal) var(--ease-soft),
    transform var(--t-reveal) var(--ease-soft);
  /* No will-change here. This selector matches every revealing element
     on the page at once — around two dozen — and each one would demand
     its own compositor layer while it waited its turn. */
}

.js [data-reveal="fade-up"]    { transform: translate3d(0, 24px, 0); }
.js [data-reveal="fade-left"]  { transform: translate3d(-24px, 0, 0); }
.js [data-reveal="fade-right"] { transform: translate3d(24px, 0, 0); }
.js [data-reveal="scale"]      { transform: scale(0.94); }

/* The letter in Section 2 is one block and gets its own shorter, gentler
   move: a 16px lift over 600ms. It is a lot of text, so the less it has
   to repaint on the way in, the better. */
.js [data-reveal="letter"] {
  transform: translate3d(0, 16px, 0);
  transition:
    opacity 600ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 600ms cubic-bezier(0.22, 1, 0.36, 1);
}
.js [data-reveal="fade"]       { transform: none; }

.js [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}


/* --------------------------------------------------------------------------
   6. NAVIGATION BAR
   -------------------------------------------------------------------------- */
/* A thin strip of frosted glass. The hero picture stays clearly visible
   through it — the tint is only a whisper of white. Both the tint and the
   blur strength come from config.js (nav.glassOpacity and nav.blur). */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  /* sits above the full screen menu so the X stays tappable */
  z-index: 50;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background-color: var(--color-glass);
  -webkit-backdrop-filter: blur(var(--nav-blur)) saturate(140%);
  backdrop-filter: blur(var(--nav-blur)) saturate(140%);
  /* the hairline of gold along the bottom */
  border-bottom: 1px solid var(--color-accentHairline);
  /* only the tint eases; the shadow snaps, so the frosted layer is not
     repainted for longer than it has to be */
  transition: background-color var(--t-nav) ease;
  transform: translateZ(0);
}

/* STATE B — after scrolling past the hero. Still glass, just a touch
   stronger, with the faintest lift beneath it. */
.nav.is-solid {
  background-color: var(--color-glassScrolled);
  box-shadow: 0 2px 10px var(--color-glassShadow);
}

/* If a browser cannot do frosted glass, it gets a slightly stronger
   see-through tint instead — never a solid bar. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .nav          { background-color: var(--color-glassFallback); }
  .nav.is-solid { background-color: var(--color-glassFallbackScrolled); }
}

/* While the full screen menu is open the bar goes plain, so only the
   logo and the X are left floating over the menu. */
.nav.is-menu-open {
  background-color: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.nav.is-menu-open .nav__links {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 200ms ease, visibility 200ms ease;
}

/* The writing in the bar is dark ink, because the glass sits over the
   pale top of the hero picture and over the cream of section 2.
   A soft light glow keeps it crisp. */
.nav {
  --nav-ink: var(--color-ink);
}
/* Over the dark full screen menu it flips to light instead. */

.nav__inner {
  position: relative;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--space-3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
}

/* The logo sits on the far left, sized to fit inside the thin bar
   without stretching it. */
.nav__logo {
  height: 30px;
  width: auto;
  max-width: 46px;
  object-fit: contain;
  border-radius: var(--radius-sm);
}
@media (min-width: 900px) {
  .nav__logo { height: 34px; max-width: 54px; }
}

/* If the logo file is missing, it simply disappears rather than
   showing a broken image icon. The business name stays. */
.nav__logo.is-missing { display: none; }

.nav__brandName {
  font-size: clamp(0.9rem, 0.86rem + 0.3vw, 1.05rem);
  letter-spacing: 0.02em;
  color: var(--nav-ink);
  white-space: nowrap;
  text-shadow: var(--halo-soft);
}
.nav.is-menu-open .nav__brandName { text-shadow: none; }

.nav__right {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The three inline links. Hidden on phones from the start. */
.nav__links {
  display: none;
  align-items: center;
  gap: var(--space-4);
  transition:
    opacity var(--t-nav) ease,
    transform var(--t-nav) ease,
    visibility var(--t-nav) ease;
}

@media (min-width: 900px) {
  .nav__links { display: flex; }
}

/* They fade and slide away once we scroll past the hero */
.nav.is-solid .nav__links {
  opacity: 0;
  visibility: hidden;
  transform: translate3d(12px, 0, 0);
  pointer-events: none;
}

.nav__link {
  position: relative;
  font-size: 0.9rem;
  color: var(--nav-ink);
  text-shadow: var(--halo-soft);
  padding: var(--space-1) 0;
  white-space: nowrap;
}
/* A thin gold underline that grows on hover */
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background-color: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 300ms var(--ease-soft);
}
.nav__link:hover::after,
.nav__link:focus-visible::after { transform: scaleX(1); }


/* --------------------------------------------------------------------------
   7. LANGUAGE TOGGLE (EN / मराठी)

   A two-part pill. Both languages are on screen at all times and a small
   cream knob slides across to sit behind the one being read — so the
   control shows its state instead of asking you to work it out.

   The track is the same frosted material as the nav bar, so the pill
   reads as part of the glass strip rather than a button dropped onto it.
   -------------------------------------------------------------------------- */
.langToggle {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  height: 34px;
  padding: 3px;
  border-radius: var(--radius-pill);
  line-height: 1;

  /* A plain tint, not a live blur. backdrop-filter forces the browser to
     re-sample and re-blur whatever is behind this pill on every frame it
     moves over — and it sits in a fixed bar over a scrolling page, so
     that is every frame. The flat tint is all but indistinguishable. */
  background-color: var(--color-pillTrack);
  border: 1px solid var(--color-accentHairline);
}

/* The visible pill is 34px so it sits comfortably inside the thin bar,
   but the finger target is a full 44px. This invisible strip is what
   makes up the difference. */
.langToggle::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

/* ---- the sliding knob ---- */
/* It is positioned against the track's padding box, so 50% is exactly
   one segment and translateX(100%) lands it on the other. */
.langToggle__knob {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc(50% - 3px);
  border-radius: var(--radius-pill);
  background-color: var(--color-pillKnob);
  box-shadow: 0 1px 3px var(--color-shadow);
  /* deliberately NOT animated yet — see .is-ready below */
}

/* The knob only starts sliding once the page has settled. A guest who
   chose Marathi last time arrives with the knob already on the right,
   rather than watching it travel there on load. */
.langToggle.is-ready .langToggle__knob {
  transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
}

.langToggle.is-mr .langToggle__knob { transform: translateX(100%); }

/* ---- the two words ---- */
/* Both keep the same weight in both states. Only colour separates them,
   which means the pill cannot change width as the knob moves. */
.langToggle__seg {
  position: relative;
  z-index: 1;                 /* above the knob */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.45rem;
  white-space: nowrap;
  font-weight: 500;
  /* The colour alone marks this word as the one NOT being read. It used
     to be faded as well, which pushed it under readable contrast for no
     extra clarity — the active word sits on a solid knob and is already
     unmistakable. */
  color: var(--color-inkSoft);
  text-shadow: var(--halo-soft);
  transition: color 300ms ease, opacity 300ms ease;
}

.langToggle__seg.is-active {
  color: var(--color-ink);
  opacity: 1;
  text-shadow: none;          /* it is on the solid knob now */
}

/* Latin: small and letter-spaced, the way the rest of the site sets
   its small caps. */
.langToggle [data-lang-seg="en"] {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Devanagari: never letter-spaced — it pulls the joined letters apart —
   and set a size larger than the Latin so the two look the same weight.
   At matching font-sizes the Devanagari reads noticeably smaller. */
.langToggle [data-lang-seg="mr"] {
  font-family: "Tiro Devanagari Marathi", "Noto Serif Devanagari", system-ui, serif;
  /* Tiro has one weight, so it is asked for that one and nothing else */
  font-weight: 400;
  font-size: 0.84rem;
  letter-spacing: 0;
  text-transform: none;
}

@media (min-width: 900px) {
  .langToggle__seg { padding: 0 0.7rem; }
}

/* ---- the larger one inside the full screen menu ---- */
.langToggle--menu {
  height: 44px;
  padding: 4px;
}
.langToggle--menu .langToggle__knob {
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: calc(50% - 4px);
}
.langToggle--menu .langToggle__seg {
  padding: 0 0.95rem;
  text-shadow: none;          /* the menu is a flat cream panel */
}
.langToggle--menu [data-lang-seg="en"] { font-size: 0.8rem; }
.langToggle--menu [data-lang-seg="mr"] { font-size: 0.96rem; }

/* Over the open menu the bar's pill needs no glow behind it */
.nav.is-menu-open .langToggle__seg { text-shadow: none; }


/* --------------------------------------------------------------------------
   8. HAMBURGER BUTTON
   The three lines animate into an X — no icon swapping.
   -------------------------------------------------------------------------- */
.burger {
  position: relative;
  width: 44px;
  height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  z-index: 45;
}

.burger__line {
  display: block;
  width: 22px;
  height: 2px;
  border-radius: 2px;
  background-color: var(--nav-ink);
  transition:
    transform 300ms var(--ease-soft),
    opacity 200ms ease,
    background-color var(--t-nav) ease;
}

/* Open state: top line rotates down, middle disappears, bottom rotates up */
.burger.is-open .burger__line:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.burger.is-open .burger__line:nth-child(2) { opacity: 0; transform: scaleX(0.4); }
.burger.is-open .burger__line:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }


/* --------------------------------------------------------------------------
   9. FULL SCREEN MENU
   -------------------------------------------------------------------------- */
.menu {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  padding: calc(var(--nav-h) + var(--space-4)) var(--space-3) var(--space-5);
  background-color: var(--color-menuBg);
  opacity: 0;
  transition: opacity var(--t-menu) ease;
}

/* Completely out of the way when closed (also keeps it off screen readers) */
.menu[hidden] { display: none; }

.menu.is-open { opacity: 1; }

.menu__links {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
}

.menu__link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: var(--space-1) var(--space-3);
  font-size: clamp(1.75rem, 1.3rem + 3.2vw, 2.75rem);
  line-height: 1.25;
  color: var(--color-ink);
  /* each link starts slightly low and invisible, then rises in */
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition:
    opacity 450ms var(--ease-soft),
    transform 450ms var(--ease-soft),
    color 250ms ease;
}
.menu__link:hover { color: var(--color-primary); }

/* When the menu is open the links arrive one after another (60ms apart) */
.menu.is-open .menu__link { opacity: 1; transform: none; }
.menu.is-open .menu__link:nth-child(1) { transition-delay: 120ms; }
.menu.is-open .menu__link:nth-child(2) { transition-delay: 180ms; }
.menu.is-open .menu__link:nth-child(3) { transition-delay: 240ms; }
.menu.is-open .menu__link:nth-child(4) { transition-delay: 300ms; }
.menu.is-open .menu__link:nth-child(5) { transition-delay: 360ms; }

/* The language switch sits below, separated by a hairline */
.menu__langWrap {
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-accentLine);
  width: min(220px, 60%);
  display: flex;
  justify-content: center;
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity 450ms var(--ease-soft), transform 450ms var(--ease-soft);
}
.menu.is-open .menu__langWrap {
  opacity: 1;
  transform: none;
  transition-delay: 440ms;
}

/* ---- WhatsApp / Instagram / Gmail: the quiet footer of the menu ---- */
.social {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-accentHairline);
  width: min(260px, 70%);
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity 450ms var(--ease-soft), transform 450ms var(--ease-soft);
}
.social[hidden] { display: none; }

.menu.is-open .social {
  opacity: 1;
  transform: none;
  transition-delay: 520ms;
}

/* Each glyph sits in its own small disc of frosted glass with a gold
   hairline ring — the same material as the top bar, so the whole site
   speaks one language. */
.social__link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  /* Flat fill, no live blur: there are nine of these discs and each one
     was its own continuously re-blurred layer. */
  background-color: var(--color-discFill);
  box-shadow: inset 0 0 0 1px var(--color-accentHairline);
  transition:
    transform 260ms var(--ease-soft),
    box-shadow 260ms ease;
}
.social__link[hidden] { display: none; }

/* Press: a small settle inward and the ring brightens for a moment.
   No colour flash, no bounce. */
.social__link:hover,
.social__link:focus-visible {
  box-shadow: inset 0 0 0 1px var(--color-accentRing);
}
.social__link:active {
  transform: scale(0.94);
  box-shadow: inset 0 0 0 1px var(--color-accentRing);
}

/* Every mark is sized individually so they carry the same visual
   weight rather than the same pixel count. A solid disc reads bigger
   than an open shape, and a wide mark reads bigger than a square one,
   so the roundest ones are set smallest.

   All six are listed. Leaving any of them out lets its SVG fall back to
   filling the whole 44px disc edge to edge. */
.social__icon { display: block; }
.social__icon--whatsapp  { width: 22px; height: 22px; }
.social__icon--instagram { width: 21px; height: 21px; }
.social__icon--facebook  { width: 21px; height: 21px; }
.social__icon--youtube   { width: 22px; height: 22px; }
.social__icon--gmail     { width: 20px; height: 20px; }
.social__icon--website   { width: 21px; height: 21px; }


/* --------------------------------------------------------------------------
   10. HERO
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  /* Exactly one screen — not a minimum, not auto. The chain lets older
     browsers fall back: vh, then dvh, then svh, which is the one that
     stays steady as mobile browser bars slide away. */
  height: 100vh;
  height: 100dvh;
  height: 100svh;
  display: flex;
  flex-direction: column;
  /* the words now sit near the TOP of the picture */
  justify-content: flex-start;
  overflow: hidden;
  isolation: isolate;
}

/* The picture layer sits at the bottom of the hero, with the words
   stacked above it. Everything uses positive layer numbers so no part
   of it can ever slip behind the page background. */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  /* a solid colour block shows through if the photo is missing */
  background-color: var(--color-surface);
}

.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The very slow Ken Burns zoom on the photo version */
.hero__media img {
  transform-origin: center;
  animation: kenburns 20s ease-out forwards;
}

@keyframes kenburns {
  from { transform: scale(1); }
  to   { transform: scale(1.08); }
}

/* NOTE: there is deliberately no dark veil over the hero any more.
   The picture is shown clean from top to bottom. The words stay
   readable through their own soft light glow instead. */

.hero__content {
  position: relative;
  z-index: 2;                 /* above the picture */
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  /* just below the thin bar, with room to breathe */
  padding: calc(var(--nav-h) + var(--space-4)) var(--space-3) 0;
  text-align: center;
}

.hero__headline {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
}

.hero__family {
  font-size: clamp(1.3rem, 1rem + 2.1vw, 2.1rem);
  line-height: 1.25;
  color: var(--color-ink);
  text-shadow: var(--halo);
  letter-spacing: 0.01em;
}

.hero__connector {
  font-family: "Inter", system-ui, sans-serif;
  font-size: clamp(0.72rem, 0.66rem + 0.6vw, 0.9rem);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-ink);
  text-shadow: var(--halo);
  line-height: 1.5;
}
/* Marathi should not be forced into capitals or wide letter spacing */

/* The biggest, most confident line. Kept a little smaller than before
   so it never crowds Bappa's crown on a narrow phone. */
.hero__occasion {
  font-size: clamp(2.1rem, 1.2rem + 6.4vw, 4.5rem);
  line-height: 1.12;
  color: var(--color-ink);
  text-shadow: var(--halo);
}


/* --------------------------------------------------------------------------
   11. SCROLL CUE
   -------------------------------------------------------------------------- */
/* Centred with margins rather than a transform. This element also
   carries data-reveal, and the reveal engine sets transform on what it
   animates — a centring transform here would be wiped out and the cue
   would drift to the right. */
.scrollCue {
  position: absolute;
  z-index: 2;                 /* above the picture */
  left: 0;
  right: 0;
  margin-left: auto;
  margin-right: auto;
  width: max-content;
  bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  pointer-events: none;
}

/* The cue sits on the bare picture, so it gets the soft light glow.
   The negative right margin cancels the trailing space that letter
   spacing adds after the last letter — without it the word sits
   visibly left of centre and the hairline looks off to the right. */
.scrollCue__text {
  font-size: 0.65rem;
  letter-spacing: 0.28em;
  margin-right: -0.28em;
  text-transform: uppercase;
  color: var(--color-inkSoft);
  text-shadow: var(--halo);
}

/* A thin line that travels downward on a loop */
/* A single hairline, and one small gold drop that travels down it and
   fades out, then waits a beat before it comes again. */
.scrollCue__line {
  position: relative;
  display: block;
  width: 1px;
  height: 44px;
  border-radius: 1px;
  background-color: var(--color-accentFaint);
}

.scrollCue__drop {
  position: absolute;
  left: 50%;
  top: 0;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: var(--color-accent);
  /* its own small glow, so it holds up over the pale watercolour */
  box-shadow: 0 0 6px var(--color-halo), 0 0 12px var(--color-halo);
  opacity: 0;
  animation: cueDrop 2.4s var(--ease-soft) infinite;
}

@keyframes cueDrop {
  0%   { transform: translate3d(-50%, 0, 0);    opacity: 0; }
  14%  { opacity: 1; }
  62%  { transform: translate3d(-50%, 44px, 0); opacity: 0; }
  /* the rest of the cycle is a pause, so it breathes */
  100% { transform: translate3d(-50%, 44px, 0); opacity: 0; }
}

/* Once the guest scrolls, the cue fades away for good */
.scrollCue.is-gone {
  opacity: 0 !important;
  transition: opacity 500ms ease;
}

/* The invisible marker the nav bar watches */
.hero__sentinel {
  position: absolute;
  bottom: 0; left: 0;
  width: 100%;
  height: 1px;
  pointer-events: none;
}


/* --------------------------------------------------------------------------
   12. FALLING ROSE PETALS
   A fixed layer over the whole page. It never blocks a tap.
   -------------------------------------------------------------------------- */
.petals {
  position: fixed;
  inset: 0;
  z-index: 5;
  overflow: hidden;
  pointer-events: none;
}

/* ONE ELEMENT PER PETAL.

   This used to be three nested elements each carrying will-change — one
   to fall, one to sway, one to spin — which is 42 compositor layers for
   14 petals. A phone runs out of GPU memory long before that, and when
   it does the compositor simply stops animating: the petals freeze.

   Everything now happens in a single keyframe animation on a single
   element, moving one property. The fall, the sway and the spin are all
   folded into one transform, and each petal's individual character comes
   from custom properties written once by JavaScript and never touched
   again. No will-change: an element with a running transform animation
   is promoted by the browser anyway, for exactly as long as it needs. */
.petal {
  position: absolute;
  top: 0;
  left: var(--petal-x);
  width: var(--petal-size);
  height: var(--petal-size);
  opacity: var(--petal-op);
  background-image: var(--petal-src);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  animation: petalFall var(--petal-fall) linear var(--petal-delay) infinite;
}

/* The sway is the x term, the fall is the y term and the spin is the
   rotation — one property, one animation, one layer. */
@keyframes petalFall {
  0%   { transform: translate3d(0, -20vh, 0) rotate(0deg); }
  25%  { transform: translate3d(var(--petal-sway), 14vh, 0)
                    rotate(calc(var(--petal-spin) * 0.25)); }
  50%  { transform: translate3d(0, 48vh, 0)
                    rotate(calc(var(--petal-spin) * 0.5)); }
  75%  { transform: translate3d(calc(var(--petal-sway) * -1), 82vh, 0)
                    rotate(calc(var(--petal-spin) * 0.75)); }
  100% { transform: translate3d(0, 115vh, 0) rotate(var(--petal-spin)); }
}

/* Everything stops when the guest switches to another browser tab.
   One rule on the parent, inherited by every petal. */
.petals.is-paused .petal { animation-play-state: paused; }


/* ---- the hint above the music button ----
   Guests do not always notice the button, or realise the invitation has
   music at all. This says so, once, and then goes for good the moment
   they touch anything.

   It is fixed to the same corner as the button and right-aligned to it,
   and carries the same soft light glow the rest of the site uses, so it
   stays readable over the hero photograph, the cream sections and the
   ivory footer alike. */
.musicHint {
  position: fixed;
  right: var(--space-3);
  /* directly above the button, with room to breathe */
  bottom: calc(var(--space-3) + 58px + 0.75rem);
  z-index: 30;
  margin: 0;
  max-width: 60vw;
  text-align: right;
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--color-ink);
  text-shadow: var(--halo-ring), var(--halo);
  pointer-events: none;         /* never in the way of a tap */
  opacity: 1;
  transition: opacity 400ms ease;
}

.musicHint[hidden] { display: none; }

/* the guest has touched something: it fades once and is then removed */
.musicHint.is-gone { opacity: 0; }


/* --------------------------------------------------------------------------
   13. MUSIC BUTTON
   -------------------------------------------------------------------------- */
/* A solid teal circle pinned to the bottom-right of the screen. It is a
   direct child of <body>, so nothing on the page — not the petals, not a
   section background, not the standing figure — can scroll it away or
   cover it. Only the full screen menu goes over it. */
.music {
  position: fixed;
  right: calc(var(--space-3) + env(safe-area-inset-right, 0px));
  bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  z-index: 45;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background-color: var(--color-primary);
  border: 0;
  /* one soft, low, slightly offset shadow so it lifts off the paper */
  box-shadow: 0 6px 16px var(--color-musicShadow);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 200ms var(--ease-soft);
}
.music:active { transform: scale(0.94); }
.music[hidden] { display: none; }

.music__icon {
  position: absolute;
  width: 20px;
  height: 20px;
  fill: var(--color-bg);
  transition: opacity 200ms ease;
}

/* A triangle centred by its box always looks a little left of centre,
   because its visual weight sits toward the flat edge. Nudge it right
   until it looks right. */
.music__icon--play { transform: translateX(1.5px); }

/* Cross-fade between the two icons. No rotation, no bounce — they simply
   trade places over 200ms. */
.music__icon--pause { opacity: 0; }
.music.is-playing .music__icon--play  { opacity: 0; }
.music.is-playing .music__icon--pause { opacity: 1; }

/* A soft slow ring that pulses only while music is playing */
.music__pulse {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid var(--color-primary);
  opacity: 0;
  pointer-events: none;
}
.music.is-playing .music__pulse {
  animation: musicPulse 2.4s ease-out infinite;
}

@keyframes musicPulse {
  0%   { transform: scale(1);   opacity: 0.4; }
  100% { transform: scale(1.5); opacity: 0; }
}


/* --------------------------------------------------------------------------
   14. SECTION 2 — THE INVITATION
   A white letter card sitting on cream, framed by lotus garlands.
   -------------------------------------------------------------------------- */
/* ONE background picture for the whole section — shown once, never
   repeated, never mirrored, stretching to cover however tall the
   writing makes the section. Nothing is layered on top of it. */
.invitation {
  position: relative;
  background-color: var(--color-bg);   /* only shows if the picture is missing */
  /* the top and bottom padding IS the fade zone */
  padding: var(--fade-pad) 0;
}

.invitation__inner {
  display: flex;
  justify-content: center;
  padding: 0 var(--space-2);
}

/* ---- the letter, sitting straight on the picture ---- */
.letter {
  /* wider than the other blocks: justified text needs room, and a
     narrow column is what produces stretched word gaps */
  width: 90%;
  max-width: 600px;
  color: var(--color-ink);
  text-align: left;

  /* NO GLOW WHILE IT MOVES.
     A text-shadow on a long message forces every glyph to be re-drawn on
     every frame of the fade, which was enough to stall the petals. The
     halo is added once the block has settled, by .is-settled below, and
     from then on it never repaints again. */
  text-shadow: none;
}

/* the soft light glow, applied only after the block has stopped moving */
.letter.is-settled { text-shadow: var(--halo); }
.letter.is-settled .shloka {
  text-shadow:
    0 0 4px var(--color-halo),
    0 0 9px var(--color-halo),
    0 0 18px var(--color-halo),
    0 0 34px var(--color-halo);
}

/* ---- the Sanskrit invocation at the top of the letter ----
   Always Devanagari, never translated, never touched by the language
   button. It keeps Noto Serif Devanagari at 700 — scripture, not part of
   the letter's voice, so the Playfair/Tiro switch does not touch it. */
.shloka {
  margin: 0 0 var(--space-4);
  text-align: center;
  color: var(--color-shloka);
  font-family: "Noto Serif Devanagari", serif;
  font-weight: 700;
  /* its brighter glow is applied by .letter.is-settled, once the fade
     has finished — see the note on .letter above */
}

/* LINE 1 — the invocation. It stands on its own, and the real work here
   is the space beneath it: a deliberate pause separating this single
   utterance from the couplet below, so the three lines never read as one
   stacked block. */
.shloka__invocation {
  display: block;
  font-size: clamp(1.2rem, 1.08rem + 1.02vw, 1.55rem);
  line-height: 1.7;
  margin-bottom: 1.6em;
}

/* LINES 2 AND 3 — the couplet. The same size as each other, with
   generous leading so the two halves read as one verse. Larger and more
   present than before, but held at roughly 0.83 of the invocation so
   line 1 is still plainly the largest. Both halves stay on a single
   line right down to a 360px screen. */
.shloka__line {
  display: block;
  font-size: clamp(1.03rem, 0.96rem + 0.63vw, 1.26rem);
  line-height: 1.85;
}

/* the small pause between the invocation and the message — the same
   mark used in section 4, kept smaller because it separates two parts
   of one message rather than two sections */
.divider--inline {
  width: min(180px, 55%);
  margin: var(--space-4) auto var(--space-5);
  gap: var(--space-2);
}
.divider--inline .divider__mark { width: 5px; height: 5px; }

/* ---- the opening line ---- */
/* THE LINE THAT INTRODUCES THE LETTER.
   A heading, not a paragraph: centred, never indented, never justified,
   and given clear air beneath it before the writing begins. */
.letter__subheading {
  margin: 0 0 var(--space-5);
  text-align: center;
  text-indent: 0;
  /* keeps the three lines even instead of leaving one word alone
     on the last line; browsers that do not know it simply ignore it */
  text-wrap: balance;
  font-size: clamp(1.14rem, 1rem + 1.15vw, 1.5rem);
  line-height: 1.45;
}
.letter__subheading[hidden] { display: none; }


/* ---- the body of the letter ---- */
.letter__body { margin: 0; }

/* THE BODY OF THE LETTER, set like a page from a book: justified, with
   the first line of every paragraph indented.

   Justification in a narrow column pulls word gaps apart, so English
   gets hyphenation and "text-wrap: pretty" to give the browser somewhere
   to break. See the Marathi rule below for why Devanagari does not. */
.letter__para {
  margin: 0 auto var(--space-4);
  font-size: clamp(1.02rem, 0.96rem + 0.38vw, 1.13rem);
  line-height: 1.8;
  text-align: justify;
  text-indent: 1.6em;
  hyphens: auto;
  -webkit-hyphens: auto;
  text-wrap: pretty;
  /* keeps the line length comfortable on a wide screen */
  max-width: 56ch;
  overflow-wrap: break-word;
}
.letter__para:last-child { margin-bottom: 0; }

/* MARATHI IS JUSTIFIED HERE TOO, and set larger than the English.

   Three things make that work rather than tear the column apart:
   the size goes up so fewer, longer words fill each line; the column is
   allowed to run wider than the English one; and the word gaps are
   pulled in very slightly so justification has less slack to take up.

   Hyphenation is switched OFF. Devanagari has no meaningful hyphenation
   rules and a browser guessing at them breaks words in the wrong place. */
.letter__para.is-marathi {
  font-size: clamp(1.14rem, 1.07rem + 0.42vw, 1.26rem);
  /* Tiro's matras sit high above the headline and its descenders hang
     low, so it needs more leading than the Latin at the same size. */
  line-height: 2.05;
  text-align: justify;
  text-wrap: pretty;

  /* THE THREE THINGS THAT MAKE DEVANAGARI JUSTIFY CLEANLY.
     All of them attack the same problem: a narrow column holds only
     five or six Marathi words, so every word gap has to absorb a large
     share of the leftover space, and one unlucky line opens into a
     river of white.

     1. The column runs wider than the English one. These paragraphs
        step outside the letter's own width by 4% on each side, which
        buys roughly one more word per line. */
  width: 108%;
  margin-left: -4%;
  margin-right: -4%;
  max-width: 52ch;

  /*  2. The word gaps are pulled in very slightly. This is not a visual
        tightening — it is packing: narrower gaps let one more word onto
        the line, which leaves less slack for justification to take up.
        Measured at 360px, -0.04em was the best value; more made it
        worse again. */
  word-spacing: -0.04em;

  /*  3. Hyphenation stays OFF. Devanagari has no meaningful hyphenation
        rules and a browser guessing at them breaks words in the wrong
        place — so English keeps hyphens: auto and Marathi does not. */
  hyphens: none;
  -webkit-hyphens: none;
}

/* ---- the closing line ---- */
.letter__closing {
  margin: var(--space-5) 0 0;
  text-align: center;
  text-indent: 0;
  font-size: clamp(1.13rem, 0.9rem + 1.62vw, 1.58rem);
  line-height: 1.4;
  color: var(--color-accentInk);
}
/* the closing line grows with the paragraphs, and stays centred */
.letter__closing.is-marathi {
  font-size: clamp(1.24rem, 0.99rem + 1.78vw, 1.74rem);
  line-height: 1.75;
}


/* --------------------------------------------------------------------------
   15. SECTION 3 — THE SCHEDULE

   An ordinary section at its natural height. It used to be a pinned
   stage with three characters marching across a scroll runway; all of
   that is gone.

   What is left is one quiet idea: a lotus travels down a gold line, and
   each moment's writing arrives as it gets there. One group of figures
   stands on the meadow below, and never moves.

   The picture behind is cream at the top and a blue watercolour splash
   lower down. Everything here is held inside the cream — JavaScript
   shrinks the block a little on a short screen rather than let a word
   reach the blue.
   -------------------------------------------------------------------------- */
.schedule {
  position: relative;
  background-color: var(--color-bg);   /* only shows if the picture is missing */
  /* the empty space below the writing that the group stands in */
  padding-bottom: var(--figure-space, 46vh);
}

/* the cream upper area, where the writing is allowed to live */
.schedule__safe {
  position: relative;
  z-index: 2;                /* above the standing group */
  /* A shorter top pad than the other sections use. The writing here is
     larger now, and every pixel spent above it is a pixel the fitter
     would otherwise take back out of the type. */
  padding: clamp(72px, 14vw, 104px) var(--space-3) 0;
}

/* ---- the group standing on the meadow ----
   Anchored to the bottom of the section and centred. It never moves,
   never transitions and carries no will-change: it is a still picture. */
.schedule__figure {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin-inline: auto;
  width: 100%;
  max-width: var(--figure-max, 380px);
  height: auto;
  z-index: 1;
  pointer-events: none;
  /* the nudge that sets its feet on the green meadow strip */
  transform: translateY(var(--figure-lift, 0%));
}

/* ---- the timeline block: centred as a group, read left to right ---- */
.timeline {
  --rail-x: 8%;                       /* how far in from the left the line sits */
  --marker: clamp(40px, 11vw, 48px);  /* how big the lotus is */
  position: relative;
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  /* On a short screen the cream strip is shallow. Rather than let the
     writing spill into the blue, JavaScript shrinks the whole block a
     little. On a normal phone this stays at 1. */
  transform: scale(var(--timeline-scale, 1));
  /* top LEFT, not top centre: if the fitter ever has to shrink this
     block, it must stay anchored to the left where it belongs rather
     than sliding back toward the middle of the screen. */
  transform-origin: top left;
}

@media (min-width: 700px) {
  /* The block is already capped at 760px and centred, so on a wide
     screen the group stays left-aligned inside that column rather than
     drifting out to the edge of the window. */
  .timeline { --rail-x: 9%; }
}

/* ---- the line ---- */
.timeline__rail {
  position: absolute;
  left: var(--rail-x);
  /* It starts at the FIRST lotus and ends at the LAST, so it can never
     poke out above the first flower or run on past the third. */
  top: var(--rail-top, 0px);
  height: var(--rail-h, 100%);
  width: 2px;
  transform: translateX(-50%);
  border-radius: 2px;
  /* the dormant track, barely there */
  background-color: var(--color-railTrack);
}

/* The ink itself, in teal — the colour that carries structure on this
   site.

   It is drawn with a single scaleY from the top, written straight from
   the scroll position. Scaling is a compositor-only operation, so the
   fill is perfectly even at any scroll speed; animating height instead
   would force a layout on every frame, which is what made the old draw
   look uneven and stuttery.

   The short transition is only there to smooth the gap between frames —
   long enough to read as ink flowing, short enough never to lag behind
   the guest's finger. */
.timeline__ink {
  position: absolute;
  inset: 0;
  border-radius: 2px;
  background: linear-gradient(
    to bottom,
    var(--color-primary) 0%,
    var(--color-primary) 45%,
    var(--color-primaryFade) 100%
  );
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---- the three lotuses ----
   One per moment, and pure artwork: no bloom, no fade, no scale, no
   travel, no transition of any kind. Each simply sits centred on the
   line and level with its own title. */
.stop__lotus {
  position: absolute;
  left: var(--rail-x);
  top: var(--lotus-top, 0px);
  width: var(--marker);
  height: auto;
  z-index: 2;
  pointer-events: none;
  transform: translate(-50%, -50%);
}

/* ---- the moments ---- */
.timeline__stops {
  list-style: none;
  margin: 0;
  padding: 0;
}

.stop {
  position: relative;
  min-height: var(--marker);
  /* Clears the line, then half a lotus, then a real gap — so the words
     can never touch a flower no matter how the marker is sized. */
  padding-left: calc(var(--rail-x) + var(--marker) * 0.5 + var(--space-2));
}

/* the gap between moments is deliberately much larger than the gaps
   inside one — that contrast is what gives the list its structure */
.stop + .stop { margin-top: clamp(2.25rem, 7.5vw, 3.5rem); }

/* ---- the writing arriving ----
   Each moment fades up as the lotus reaches it, its three lines 80ms
   apart. Once shown it stays shown: the class is only ever added, so
   scrolling back up leaves everything standing. */
.js .stop__title,
.js .stop__date,
.js .stop__time {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition:
    opacity 600ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 600ms cubic-bezier(0.22, 1, 0.36, 1);
}

.stop.is-shown .stop__title,
.stop.is-shown .stop__date,
.stop.is-shown .stop__time {
  opacity: 1;
  transform: none;
}
.stop.is-shown .stop__date { transition-delay: 80ms; }
.stop.is-shown .stop__time { transition-delay: 160ms; }

/* No glow behind these three: they move as they arrive, and a text
   shadow on moving text has to be repainted every frame. They sit on
   plain cream, so colour alone carries them. */
/* These three are the moments of the festival, not body copy. They now
   sit clearly above the address in Section 4 in the hierarchy, while
   staying below "Memories" and the hero so nothing competes. */
.stop__title {
  margin: 0;
  /* Halfway back from the last increase: clearly the most important
     line in each stop and well above body text, but no longer reading
     as a headline against "Memories" or the hero. */
  font-size: clamp(1.2rem, 0.94rem + 1.45vw, 1.75rem);
  line-height: 1.25;
  color: var(--color-ink);
}
.stop__title.is-marathi {
  /* Devanagari sets taller and runs longer, so it is held a little
     smaller and given more leading. */
  font-size: clamp(1.12rem, 0.87rem + 1.38vw, 1.65rem);
  line-height: 1.45;
}

.stop__date {
  margin: 0.45em 0 0;
  font-size: clamp(0.92rem, 0.86rem + 0.38vw, 1.04rem);
  line-height: 1.5;
  color: var(--color-inkSoft);
}

/* the quiet detail: small, wide, gold */
.stop__time {
  margin: 0.75em 0 0;
  font-size: 0.74rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--color-accentInk);
}
.stop__time.is-marathi {
  text-transform: none;
  letter-spacing: 0.1em;
  font-size: 0.81rem;
}


/* --------------------------------------------------------------------------
   16. SECTION 4 — LOCATION AND COUNTDOWN
   Plain cream and a lot of air. Almost all of the work here is done by
   the spacing between things rather than by anything drawn.
   -------------------------------------------------------------------------- */
.place {
  position: relative;                 /* the confetti canvas sits on this */
  background-color: var(--color-bg);
  padding: var(--fade-pad) var(--space-3);
}

.place__inner {
  position: relative;
  z-index: 1;                /* above the picture layer */
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  text-align: center;
}

.place__heading {
  margin: 0;
  font-size: clamp(1.9rem, 1.4rem + 2.4vw, 2.9rem);
  line-height: 1.15;
  color: var(--color-ink);
  letter-spacing: 0.01em;
}

/* the one quiet mark under the heading */
.rule {
  display: block;
  width: 60px;
  height: 1px;
  margin: var(--space-3) auto 0;
  background-color: var(--color-accent);
}

/* ---- the address ---- */
.place__address {
  display: block;
  margin-top: var(--space-4);
  font-style: normal;                 /* <address> is italic by default */
}

.place__venue {
  display: block;
  font-size: clamp(1.15rem, 1rem + 0.9vw, 1.45rem);
  line-height: 1.35;
  color: var(--color-ink);
  text-shadow: var(--halo);
}

.place__line {
  display: block;
  margin-top: 0.35em;
  font-size: clamp(0.9rem, 0.86rem + 0.3vw, 1rem);
  line-height: 1.6;
  color: var(--color-inkSoft);
  text-shadow: var(--halo);
}

/* ---- the map card ---- */
.mapCard {
  position: relative;
  width: 86%;
  max-width: 520px;
  margin: clamp(2.25rem, 8vw, 3.5rem) auto 0;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md);
  overflow: hidden;
  background-color: var(--color-surface);
  box-shadow:
    inset 0 0 0 1px var(--color-accentHairline),
    0 14px 34px var(--color-shadow);
}

.mapCard__frame {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Sits underneath the map. If the map never loads, this is what the
   guest sees — the venue name on the warm ivory, never a blank box. */
.mapCard__fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  text-align: center;
}
.mapCard__fallbackName {
  font-size: clamp(1.1rem, 1rem + 0.8vw, 1.35rem);
  color: var(--color-ink);
}

/* The transparent layer over the whole card that catches the tap. */
.mapCard__tap {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding: var(--space-2);
}

/* the small "Open in Maps" pill, in the same frosted glass as the bar */
.mapCard__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  min-height: 34px;
  padding: 0.4rem 0.8rem;
  border-radius: var(--radius-pill);
  background-color: var(--color-glassScrolled);
  box-shadow: inset 0 0 0 1px var(--color-accentHairline);
  color: var(--color-ink);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  transition: box-shadow 250ms ease, transform 250ms var(--ease-soft);
}

.mapCard__tap:hover .mapCard__pill,
.mapCard__tap:focus-visible .mapCard__pill {
  box-shadow: inset 0 0 0 1px var(--color-accentRing);
}
.mapCard__tap:active .mapCard__pill { transform: scale(0.96); }

.mapCard__pin { width: 14px; height: 14px; flex: none; }

/* ---- the pause between the map and the countdown ---- */
.divider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin: clamp(3.5rem, 12vw, 6rem) auto;
  width: min(360px, 80%);
}
.divider__line {
  flex: 1;
  height: 1px;
  background: linear-gradient(to right, transparent, var(--color-accentLine), transparent);
}
.divider__mark {
  width: 7px;
  height: 7px;
  flex: none;
  background-color: var(--color-accent);
  transform: rotate(45deg);           /* a small diamond */
}

/* ---- the countdown ---- */
/* The heading over the numbers. This is a real heading, not a caption:
   it is set in the display face at heading size, and it is the line that
   tells the guest what the four circles are counting. */
.countdown__label {
  margin: 0;
  font-family: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  font-weight: 700;
  font-size: clamp(1.4rem, 1.05rem + 1.7vw, 2.1rem);
  line-height: 1.25;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--color-accentInk);
  text-shadow: var(--halo);
}

.countdown__row {
  /* One value drives the circle, the number inside it and the width of
     the column, so the four can never fall out of step.
     At 360px this is 68px: four of those plus three gaps is 310px in a
     324px column, which leaves the row comfortably inside the screen. */
  --disc: clamp(64px, 19vw, 92px);

  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(0.75rem, 3.4vw, 1.5rem);
  margin-top: var(--space-4);
}

/* Four across, always — never wrapping to two rows. The column is tied
   to the circle's width so a long word underneath cannot widen it. */
.countdown__unit {
  flex: 0 0 auto;
  width: var(--disc);
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Every circle is exactly the same size whatever number is inside it,
   so a three-figure day count and a one-figure second count sit in
   identical discs.

   The gold picture carries the whole look: no shadow, no ring, no glow
   is added here, because anything extra fights the artwork. */
.countdown__disc {
  position: relative;
  width: var(--disc);
  height: var(--disc);
  flex: none;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;

  /* Only ever seen if the picture in config is missing or fails to load,
     so a broken path still leaves a gold circle rather than a hole. */
  background-color: var(--color-accentSoft);

  /* The supplied artwork draws its disc inside a larger square, so it is
     zoomed until the gold reaches the edges. Both the file and the zoom
     come from config.countdown. */
  background-image: var(--countdown-circle);
  background-repeat: no-repeat;
  background-position: center;
  background-size: var(--countdown-zoom) var(--countdown-zoom);

  /* THE BROWN RING — a hairline, not an outline.
     The gold artwork already defines its own edge, so this only has to
     settle it against the cream. It is 1px at low opacity: enough to
     stop the disc floating, not enough to read as a drawn border. No
     shadow or glow anywhere near it. */
  border: var(--countdown-ring, 0px) solid var(--color-countdownRing);
  background-origin: border-box;
  background-clip: border-box;
}

/* The two digits sit side by side. Each one is its own little drum. */
.countdown__value {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  /* A rounded, friendly sans, named in config so it can be changed in
     one line. Always Latin figures whichever language is showing —
     Devanagari digits are a different width and would break the row. */
  font-family: var(--font-number), "Baloo 2", system-ui, sans-serif;
  font-weight: 600;
  font-size: calc(var(--disc) * 0.40);
  line-height: 1;
  /* Deep plum on gold. Measured against the lightest part of the
     artwork it is 8.9:1, and against the darkest 4.9:1 — comfortable
     either way, where a gold or a cream number would disappear. */
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;

  /* Optically centred, not mathematically. Figures sit on the baseline
     with empty descender space beneath them, so a box centred by
     measurement reads a shade high. This sets it down onto its true
     centre. */
  transform: translateY(0.045em);
}

/* One digit. The window is only as tall as a single figure, so the
   outgoing and incoming digits appear to be on a rotating drum. */
.countdown__digit {
  position: relative;
  display: block;
  width: 0.62em;
  height: 1.12em;
  overflow: hidden;
  text-align: center;
}

.countdown__digit span {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* the digit leaving, and the one arriving from below */
.countdown__digit.is-rolling .countdown__digit-out {
  animation: digitOut 320ms var(--ease-soft) forwards;
}
.countdown__digit.is-rolling .countdown__digit-in {
  animation: digitIn 320ms var(--ease-soft) forwards;
}

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

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

/* The word under each circle. Larger than it was, but still plainly
   subordinate to the number: small caps, letter-spaced, and in the
   softer ink so it never competes with the figure above it.
   The tracking is held at 0.12em because "MINUTES" and "SECONDS" have
   to stay inside the width of their circle. */
.countdown__name {
  display: block;
  margin-top: 0.9em;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Full ink, not the softer plum: at this size, against a busy
     background, the softer colour simply could not be read. Weight and
     colour do the work here — they stay well below the digits in size,
     so the numbers are still unmistakably the loudest thing. */
  color: var(--color-ink);
  white-space: nowrap;
  /* These four sit low on the picture and the leftmost one lands on the
     painted lotus, where a third of it is over dark leaves. The tight
     ring holds the letter shapes there; the soft glow does the rest on
     the cream under the other three. */
  text-shadow: var(--halo-ring), var(--halo-soft);
}

/* the line that replaces the numbers on the day */
.countdown__done {
  margin: var(--space-4) 0 0;
  font-size: clamp(1.8rem, 1.3rem + 2.6vw, 2.9rem);
  line-height: 1.2;
  color: var(--color-ink);
}

/* the confetti, drawn by hand and removed the moment it finishes */
.confetti {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
}


/* --------------------------------------------------------------------------
   17. SECTION 5 — THE PHOTO GALLERY
   A pile of prints on a table. The guest pushes the top one aside and it
   goes to the bottom of the pile, round and round.
   -------------------------------------------------------------------------- */
/* THE SECTION'S HEIGHT NEVER CHANGES.
   Its vertical padding is split into a top and a bottom half whose sum
   is always the same, and JavaScript slides that split — see
   layoutGallery. Moving space from the top to the bottom walks the whole
   group upward off the painted lotuses without the section growing or
   shrinking by a single pixel, which also means the background's crop
   never moves under the calculation. */
.gallery {
  --gallery-shift: 0px;
  position: relative;
  background-color: var(--color-bg);   /* only shows if the picture is missing */
  /* The 22px on each side gives back exactly what tightening the inner
     gaps took away, so the section is the same overall height it has
     always been — only its contents sit higher within it. */
  padding:
    calc(var(--fade-pad) + 22px - var(--gallery-shift))
    var(--space-3)
    calc(var(--fade-pad) + 22px + var(--gallery-shift));
}

.gallery__inner {
  position: relative;
  z-index: 1;                          /* above the picture layer */
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  text-align: center;
}

/* the same treatment as Section 4's heading, so the two read as siblings */
.gallery__heading {
  margin: 0;
  font-size: clamp(1.9rem, 1.4rem + 2.4vw, 2.9rem);
  line-height: 1.15;
  color: var(--color-ink);
  text-shadow: var(--halo);
  letter-spacing: 0.01em;
}

/* ---- the stack ---- */
.stack {
  --card-w: min(74vw, 300px);
  position: relative;
  width: var(--card-w);
  /* 3:4 portrait, plus room for the edges peeking out behind */
  height: calc(var(--card-w) * 4 / 3 + 40px);
  margin: clamp(1.5rem, 5.5vw, 2.5rem) auto 0;
  perspective: 700px;
  outline: none;
}

@media (min-width: 900px) {
  .stack { --card-w: 320px; }
}

.stack:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 10px;
  border-radius: var(--radius-md);
}

.stack__card {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--card-w);
  aspect-ratio: 3 / 4;
  border-radius: var(--radius-md);
  overflow: hidden;
  background-color: var(--color-surface);
  box-shadow:
    inset 0 0 0 1px var(--color-accentHairline),
    0 12px 28px var(--color-shadow);

  /* Vertical page scrolling must keep working when a finger lands on a
     photo. pan-y hands vertical gestures back to the page and leaves
     only sideways drags to the gallery. */
  touch-action: pan-y;
  cursor: grab;

  /* Each card knows its own place in the pile through --i (1 = front).
     The ones behind sit further back and a little lower, so two or three
     edges peek out below the front photograph. */
  transform:
    translate3d(0, calc(11px * (var(--i) - 1)), calc(-22px * (var(--i) - 1)))
    rotate(0deg);
  transition:
    transform 420ms var(--ease-soft),
    opacity 420ms var(--ease-soft);
}

.stack__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;   /* the card handles the gesture, not the picture */
  user-select: none;
  -webkit-user-drag: none;
}

/* while a finger is down, the card must track it with no easing at all */
.stack__card.is-dragging {
  transition: none;
  cursor: grabbing;
  will-change: transform;
}

/* the one gentle nudge that tells the guest the pile can be pushed */
.stack__card.is-hinting {
  animation: stackNudge 600ms var(--ease-soft) 1;
}

@keyframes stackNudge {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg); }
  45%  { transform: translate3d(-14px, 0, 0) rotate(-2.4deg); }
  100% { transform: translate3d(0, 0, 0) rotate(0deg); }
}

/* ---- which photo is in front ---- */
.stack__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: var(--space-3);
}

/* The dots and the hint below them can land on any part of the
   background picture — plain cream in one place, a painted lotus in
   another. Rather than put a panel behind them, each carries the same
   soft light glow the writing uses elsewhere, which lifts them off
   whatever they happen to be sitting on. */
.stack__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--color-inkFaint);
  /* a box-shadow, not a drop-shadow filter: identical on a plain circle
     and it does not put the dot on its own compositor layer */
  box-shadow: 0 0 3px var(--color-halo), 0 0 6px var(--color-halo);
  transition: background-color 300ms ease, transform 300ms var(--ease-soft);
}
.stack__dot.is-active {
  background-color: var(--color-accent);
  transform: scale(1.35);
}

/* ---- the hint, which goes for good after the first swipe ---- */
.stack__hint {
  margin: var(--space-2) 0 0;
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-inkSoft);
  /* This line is small and sits at the very bottom of the picture,
     right where the painted lotus pond is. The soft glow alone is not
     enough there — about an eighth of it crosses dark leaves and water
     — so the tight ring goes underneath to hold the letter shapes, and
     the wide glow still does the lifting on the cream. */
  text-shadow: var(--halo-ring), var(--halo);
  transition: opacity 500ms ease;
}
.stack__hint.is-gone { opacity: 0; }

/* ---- the closing beat ----
   A thank-you and a signature, sitting well below the photographs so
   they read as the end of the invitation rather than a caption stuck to
   the stack. Both lines carry the soft glow, because they sit straight
   on the background picture with no panel behind them.

   The block reserves the taller of the two languages (JavaScript sets
   the min-height), so switching language never shifts the footer. */
.signoff {
  width: 100%;
  max-width: 30rem;
  /* The gap BETWEEN the two signoff lines is deliberate and untouched;
     only the space above the block is tightened. */
  margin: clamp(1.75rem, 6.5vw, 3rem) auto 0;
  text-align: center;
}

.signoff__thanks {
  margin: 0;
  font-size: clamp(1.07rem, 1rem + 0.43vw, 1.2rem);
  line-height: 1.65;
  color: var(--color-ink);
  text-shadow: var(--halo);
}
.signoff__thanks.is-marathi {
  font-size: clamp(0.95rem, 0.9rem + 0.35vw, 1.06rem);
  line-height: 1.9;
}

/* the signature: the display face, larger, with real air above it */
.signoff__family {
  margin: clamp(0.9rem, 3.5vw, 1.4rem) 0 0;
  /* the face is set with the rest of the signoff, near the top of the file */
  font-size: clamp(1.42rem, 1.1rem + 1.75vw, 1.98rem);
  line-height: 1.25;
  color: var(--color-ink);
  text-shadow: var(--halo);
}
.signoff__family.is-marathi {
  font-size: clamp(1.32rem, 1.03rem + 1.55vw, 1.8rem);
  line-height: 1.5;
}

/* if there are no photos at all, the whole gallery gets out of the way */
.gallery.is-empty .stack,
.gallery.is-empty .stack__dots,
.gallery.is-empty .stack__hint { display: none; }


/* --------------------------------------------------------------------------
   18. SECTION 6 — THE FOOTER
   The closing panel. A touch deeper than the page cream, so it reads as
   an ending without ever becoming a hard block.
   -------------------------------------------------------------------------- */
/* THE HEIGHT OF THIS SECTION IS DELIBERATE.

   Everything here is a sign-off, and a sign-off should be brief. The
   whole panel is roughly half the height it was, and all of that came
   out of spacing — every element it had, it still has.

   Where the space came from, in order of how much it saved:
     1. the two icon blocks now sit side by side instead of stacked
     2. the top padding no longer uses the full section fade distance —
        Section 5 above already ends with its own, so the breathing room
        between the two is unchanged
     3. each small-caps label sits close to the row it labels
     4. the gaps between blocks are tightened throughout          */
.footer {
  position: relative;
  background-color: var(--color-surface);
  padding: clamp(44px, 12vw, 72px) var(--space-3) clamp(1.5rem, 5vw, 2rem);
}

/* The ivory does not start abruptly — it emerges out of the page cream
   over about 100px, so the join with Section 5 cannot be seen. */
.footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 100px;
  background: linear-gradient(to bottom, var(--color-bg), transparent);
  pointer-events: none;
}

.footer__inner {
  position: relative;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
}

/* ---- the brand mark ---- */
.footer__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.footer__logo {
  height: 34px;
  width: auto;
  max-width: 52px;
  object-fit: contain;
  border-radius: var(--radius-sm);
}
.footer__logo.is-missing { display: none; }

.footer__name {
  font-family: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: clamp(1.25rem, 1.1rem + 0.9vw, 1.55rem);
  line-height: 1.2;
  color: var(--color-ink);
}

/* ---- the tagline ---- */
/* Close under the name — it belongs to it. */
.footer__tagline {
  margin: var(--space-2) 0 0;
  font-size: 0.7rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--color-accentInk);
}

/* ---- what you do ---- */
.footer__descriptor {
  margin: var(--space-1) 0 0;
  max-width: 42ch;
  font-size: clamp(0.92rem, 0.88rem + 0.3vw, 1.02rem);
  line-height: 1.6;
  color: var(--color-inkSoft);
}

/* ---- the two labelled icon rows, side by side ---- */
/* FOLLOW US takes the space it needs for four icons; CONTACT US asks
   only for the width of its two. If either block has no icons at all it
   hides itself and the other simply takes the row. */
.footer__blocks {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-2);
  margin-top: clamp(1.5rem, 5vw, 2rem);
}

.footer__block { margin-top: 0; }
.footer__block[hidden] { display: none; }

/* A label sits directly over the thing it labels. */
.footer__label {
  margin: 0 0 8px;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-inkSoft);
}

/* The same circular icon component as the hamburger menu — one icon
   style on the whole site. Only the alignment and the size differ here.
   44px is the floor for a finger and these sit exactly on it. */
.social--footer {
  justify-content: flex-start;
  width: auto;
  gap: 10px;
  padding-top: 0;
  border-top: 0;
  opacity: 1;
  transform: none;
  flex-wrap: wrap;
}

.social--footer .social__link {
  width: 44px;
  height: 44px;
}

/* each icon arrives just after the one before it */
.social--footer .social__link {
  opacity: 0;
  transform: translate3d(0, 10px, 0);
  transition:
    opacity 450ms var(--ease-soft),
    transform 450ms var(--ease-soft),
    box-shadow 260ms ease;
}
.footer__block.is-revealed .social__link,
html:not(.js) .social--footer .social__link { opacity: 1; transform: none; }
.footer__block.is-revealed .social__link:nth-child(1) { transition-delay: 60ms; }
.footer__block.is-revealed .social__link:nth-child(2) { transition-delay: 120ms; }
.footer__block.is-revealed .social__link:nth-child(3) { transition-delay: 180ms; }
.footer__block.is-revealed .social__link:nth-child(4) { transition-delay: 240ms; }

/* ---- the closing line ---- */
.footer__legal {
  margin: clamp(1.5rem, 5vw, 2rem) 0 0;
  text-align: center;
  font-size: 0.7rem;
  line-height: 1.8;
  color: var(--color-inkSoft);
  opacity: 0.72;
}

.footer__rights {
  display: block;
  font-size: 0.66rem;
  letter-spacing: 0.06em;
}


/* --------------------------------------------------------------------------
   19. SEAMLESS SECTION JOINS

   The page should read as one continuous sheet of paper. Two things make
   that work, and both matter:

     1. the page cream is sampled from the cream inside the pictures, so
        they are the same colour to begin with (see colors.bg in config)
     2. every picture sits on its own layer and is faded out at its top
        and bottom edges, so no picture ever just stops

   The sections are then pulled up slightly so those fades overlap rather
   than meeting edge to edge — a meeting edge is exactly where a seam shows.
   -------------------------------------------------------------------------- */
.sectionBg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  pointer-events: none;

  /* The ramp is set in pixels, not per cent, so a short section and a
     tall one dissolve over exactly the same distance on screen. */
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0px,
    black var(--fade-ramp),
    black calc(100% - var(--fade-ramp)),
    transparent 100%
  );
  mask-image: linear-gradient(
    to bottom,
    transparent 0px,
    black var(--fade-ramp),
    black calc(100% - var(--fade-ramp)),
    transparent 100%
  );
}

/* THE JOIN.
   Each picture is faded out at its own top and bottom edge by the mask
   above, and the ramp is long enough that the detail has dissolved well
   before the section ends.

   There used to be a blurred copy of the picture lying across each join
   as well — a full-width strip carrying filter: blur(16px). A large
   blurred layer is one of the most expensive things a phone GPU can be
   asked for, and there was one at every boundary. They are gone. The
   210px ramp below leaves both neighbours as pure page cream where they
   meet, so there is nothing left for a blur to hide. */
/* THE HERO.
   The hero is exactly one screen and its picture must fill that screen.
   So its dissolve is confined to the final 140px — enough to blend into
   the cream below it, short enough that the picture still reads as
   full-bleed on load. It carries no blurred bleed strip: that would wash
   out a second band of the one screen the guest sees first. */
.hero__media {
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black calc(100% - 140px), transparent 100%);
  mask-image: linear-gradient(to bottom, black 0%, black calc(100% - 140px), transparent 100%);
}

/* NO negative margins anywhere. Sections sit at their natural height
   and meet cleanly; the fade happens inside each one's own padding. */

/* The footer is a flat colour rather than a picture, so its own top
   gradient does the same job. It is shorter than it was, to match the
   footer's shorter top padding — the ivory still arrives out of the
   page cream rather than starting at a line. */
.footer::before { height: 120px; }

/* the writing has to sit above the picture layer */
.invitation__inner { position: relative; z-index: 1; }


/* --------------------------------------------------------------------------
   20. FOCUS STYLE (keyboard users)
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}


/* --------------------------------------------------------------------------
   21. REDUCED MOTION
   Some guests ask their phone to keep animation to a minimum.
   We respect that: no petals, no drifting, everything simply visible.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .petals { display: none; }

  .hero__media img { animation: none; }

  /* the scroll cue is motion and nothing else, so it goes entirely */
  .scrollCue { display: none !important; }

  .music__pulse { animation: none !important; }

  .js [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .menu__link, .menu__langWrap, .social {
    opacity: 1 !important;
    transform: none !important;
    transition-delay: 0ms !important;
  }

  /* The language knob jumps to the chosen segment instead of sliding.
     The pill still shows its state — only the travel is removed. */
  .langToggle__knob { transition: none !important; }

  /* Section 3: it is already an ordinary section, so there is nothing
     to unpin. All three moments are simply shown, the line is drawn to
     the last of them, and nothing transitions. */
  .stop__title, .stop__date, .stop__time {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0ms !important;
  }
  /* the line is simply drawn, and the lotuses never moved anyway */
  .timeline__ink { transition: none !important; }

  .timeline { transform: none !important; }
  .timeline__ink { clip-path: none !important; }


  .stop__title, .stop__date, .stop__time {
    opacity: 1 !important;
    transform: none !important;
    transition-delay: 0ms !important;
  }

  /* Section 5: no pile, no dragging, no nudge. The photographs are laid
     out plainly, two across, all of them visible at once. */
  .stack {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3);
    width: 100%;
    max-width: 520px;
    height: auto !important;
    perspective: none !important;
  }
  .stack__card {
    position: relative !important;
    width: 100% !important;
    transform: none !important;
    opacity: 1 !important;
    transition: none !important;
    animation: none !important;
    touch-action: auto;
    cursor: default;
    will-change: auto !important;
  }
  .stack__dots,
  .stack__hint { display: none !important; }

  /* the countdown digits change instantly, with no roll */
  .countdown__digit.is-rolling .countdown__digit-out,
  .countdown__digit.is-rolling .countdown__digit-in { animation: none !important; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
