/* Wedding Road — a drive down a painted ribbon.
   The ribbon carries the road and both verges as one image, so there is no
   layer boundary anywhere in the scene and nothing to blend at runtime. */

:root {
  --leg-h: 150vh;
  --asphalt: #2B2C2E;
  --leaf-deep: #25401F;
  --ground: #1b2416;
  --gold: #E8C66A;
  --blossom: #F6DDE3;
  --text: #FFFFFF;
  /* Shared by every fixed text overlay (.title, .details) — a tight dark
     layer for edge contrast against busy painted art, plus a soft wide one
     so the whole glyph reads even over a light patch of the scene. A single
     soft blur alone (the original spec) wasn't enough contrast once the
     type got large and content varied — the fixed word "and" or a event
     name can land over sky, water or a light hillside depending on scroll
     position, not just the same day-lit road every time. */
  --overlay-shadow:
    0 1px 3px rgba(0, 0, 0, .75),
    0 2px 10px rgba(0, 0, 0, .6),
    0 4px 30px rgba(10, 18, 8, .55);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--ground); }
body {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior-y: none;
  font-family: "EB Garamond", Georgia, serif;
  color: var(--text);
}

/* ---------------------------------------------------------------- the world */

.world {
  position: fixed; inset: 0; overflow: hidden; z-index: 0;
  background: var(--ground);
  /* contrast(1.113)/saturate(1.08) used to live here as a runtime filter.
     Moved into tools/build-ribbon.py's own pixel pipeline instead (baked
     into the shipped .webp) — filter on the element that also gets
     transform-animated every scroll frame forces the browser to
     re-rasterize the filtered output continuously, a real cause of scroll
     jank on mobile. Same visual result, zero per-frame cost this way. */
}

.ribbon {
  position: absolute; top: 0; left: 50%;
  will-change: transform;
  transform: translate3d(0, 0, 0);
}
/* chunks are cuts from one strip — integer tops and heights keep them hairline-free */
.ribbon img {
  position: absolute; left: 0; width: 100%;
  display: block; pointer-events: none;
}

/* tarmac motion smear, gated on measured speed */
.streaks {
  position: absolute; top: 0; left: 50%;
  height: 300vh; opacity: 0;
  pointer-events: none; mix-blend-mode: overlay;
  will-change: transform, opacity;
  /* mask-image/-size/-repeat are set from road.js, alongside the background
     that they need to line up with */
}

.vignette {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(120% 62% at 50% 8%,  rgba(255,248,226,.16), rgba(255,248,226,0) 58%),
    radial-gradient(155% 96% at 50% 104%, rgba(10,18,8,.42),   rgba(10,18,8,0)   56%);
}

/* ----------------------------------------------------------- atmosphere */

.clouds {
  position: fixed; left: -6%; right: -6%; top: 0; height: 100%;
  z-index: 6; pointer-events: none; opacity: .12;
  background-repeat: repeat-y; will-change: transform;
  background-image:
    radial-gradient(58% 24% at 22% 11%, rgba(255,252,244,.62), rgba(255,252,244,0) 70%),
    radial-gradient(43% 19% at 79% 37%, rgba(255,250,238,.48), rgba(255,250,238,0) 72%),
    radial-gradient(64% 21% at 44% 62%, rgba(255,247,234,.42), rgba(255,247,234,0) 74%),
    radial-gradient(36% 15% at 11% 85%, rgba(255,252,246,.34), rgba(255,252,246,0) 76%);
}

.tint  { position: fixed; inset: 0; z-index: 18; pointer-events: none; mix-blend-mode: soft-light; background-color: rgba(255,217,160,.14); }
.dusk  { position: fixed; inset: 0; z-index: 19; pointer-events: none; background-color: rgba(20,26,40,0); }
.grain { position: fixed; inset: 0; z-index: 20; pointer-events: none; opacity: .10; mix-blend-mode: overlay; background-size: 140px 140px; }

/* ------------------------------------------------------------------ car */

.car-anchor {
  position: fixed; top: 56%; left: 0; right: 0;
  z-index: 12; display: flex; justify-content: center;
  transform: translateY(-50%); pointer-events: none;
}
.car-drop  { animation: carIn 2.5s cubic-bezier(.28,1.12,.6,1) both; }
.car-track { will-change: transform; }
.car-idle  { filter: drop-shadow(0 8px 12px rgba(10,16,8,.45)); }
.car-idle img { display: block; width: 100%; height: auto; transform: rotate(180deg); }
/* the 3D car's canvas is sized in JS (measure()) to match the painted
   image's old width/height contract - width comes from CAR_ROAD same as
   before, height from the model's own aspect ratio once it loads, since a
   canvas has no natural aspect ratio to derive from the way an <img> does */
.car-idle canvas { display: block; }

@keyframes carIn { from { transform: translate3d(0,-125vh,0); } to { transform: translate3d(0,0,0); } }

/* --------------------------------------------------------------- content */

.legs { position: relative; z-index: 10; pointer-events: none; }
.leg  { position: relative; height: var(--leg-h); }

/* ------------------------------------------------------------------ rail */

.rail {
  /* hidden for now - v3's stops/copy aren't finalised, so the dots don't
     track anything meaningful yet (wrong count, no visible active state) */
  display: none;
}
.rail i {
  display: block; width: 6px; height: 6px; border-radius: 50%;
  background: rgba(255,255,255,.5);
  /* the ribbon runs bright turquoise and pale sand under this rail, so the dots
     need their own contrast rather than borrowing the scene's */
  box-shadow: 0 0 0 1px rgba(10,18,8,.42), 0 1px 3px rgba(10,18,8,.55);
  transition: background .35s ease, transform .35s ease, box-shadow .35s ease;
}
.rail i.on {
  background: var(--gold); transform: scale(1.5);
  box-shadow: 0 0 0 1px rgba(10,18,8,.5), 0 0 9px rgba(232,198,106,.7);
}

.cue {
  position: fixed; bottom: 5vh; left: 0; right: 0; z-index: 22;
  display: flex; justify-content: center; pointer-events: none;
  transition: opacity .5s ease;
}
.cue span {
  width: 1px; height: 44px;
  background: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,.8));
  animation: cue 2.4s ease-in-out infinite;
}
@keyframes cue { 0%,100% { opacity: .25; transform: translateY(0); } 50% { opacity: .9; transform: translateY(8px); } }

/* Opening title — hidden at scroll 0, revealed by tick() once the very first
   small scroll happens (see the .03 * vh threshold there). Starts at opacity
   0 here so there's nothing to see before the first tick() runs; JS then owns
   it from there. Not part of the ribbon: it names the two people, not a place
   the road passes through, so it doesn't scroll away with the art the way
   section captions would. */
/* align-items: flex-start + this padding, not center, keeps the text clear
   of the car — it sits fixed at 56% viewport height (.car-anchor), and a
   true vertical center overlaps that line directly. 18% leaves a clear gap
   of open road between the bottom of the text and the car. */
.title {
  position: fixed; inset: 0; z-index: 21;
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 23vh;
  text-align: center; pointer-events: none;
  opacity: 0;
  transition: opacity .6s ease;
}
.title h1 {
  margin: 0; font-family: "Mrs Saint Delafield", "EB Garamond", cursive, serif;
  font-weight: 400; font-size: clamp(4.6rem, 18vw, 10rem);
  line-height: 1.15;
  color: var(--text);
  text-shadow: var(--overlay-shadow);
}

.title .amp-word { display: none; }

/* Below this width, at the larger size above, "Bhavya & Ramcharan" no longer
   fits one line without shrinking the type back down to fight for room — so
   it breaks onto three lines instead: "Bhavya", "and", "Ramcharan" — the
   ampersand swapped for the word, which reads better standing alone than a
   lone "&" does. */
@media (max-width: 640px) {
  .title .first-name, .title .second-name, .title .amp-word { display: block; }
  .title .amp { display: none; }
  .title .amp-word {
    font-size: 0.4em; letter-spacing: .04em; margin: 0.15em 0;
  }
}

/* Event details — same fixed overlay slot as .title, one leg later: the
   title covers the opening two legs (garden-lead + garden) plus the beach
   leg, fades out once that beach leg (garden-beach.png, leg index 2)
   finishes, and this fades in behind it in the same spot — except while the
   dam leg (DAM_LEG in road.js) or the very last leg is active, where
   .dam-caption or #ending take the slot instead and this hides too. See
   tick() for the full onDam / onLast / TITLE_LAST_LEG split. Content is the
   three real events (venue + per-event date/time). */
/* Anchored from the top with padding rather than centered — .details has
   more vertical content than .title (venue line + 3 event rows), and
   centering it would let the bottom rows creep toward the car's fixed 56%
   line on shorter viewports. 12vh clears it with room to spare even at
   this block's tallest (three events, mobile line-wrap). */
.details {
  position: fixed; inset: 0; z-index: 21;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  padding-top: 16vh;
  gap: 1.1em;
  text-align: center; pointer-events: none;
  opacity: 0;
  transition: opacity .6s ease;
}

/* Venue — its own fixed layer, pinned to the bottom of the viewport rather
   than stacked into .details, so it reads as anchored to the device's own
   bottom edge (home indicator / gesture bar territory on a phone) instead of
   scaling with how tall the events list happens to be. The safe-area inset
   is what actually keeps it clear of that system UI — a bare vh value sits
   partly behind it on notched/gesture-nav phones, which is why an earlier
   version of this (bottom: 6vh, no inset) wasn't visible on a real device
   despite working in a plain desktop browser check. Same visibility timing
   as .details (tick() drives both together). */
.venue {
  position: fixed; left: 0; right: 0; z-index: 21;
  bottom: calc(3vh + env(safe-area-inset-bottom, 0px));
  text-align: center; pointer-events: none;
  opacity: 0;
  transition: opacity .6s ease;
}
.venue p { margin: 0; }
.details p { margin: 0; }
/* Typography follows the original brief: the venue is small uppercase white
   letterspaced 0.36em, ceremony names EB Garamond 500, times EB Garamond
   italic warm off-white (chats/chat1.md). */
.details-place {
  font-family: "EB Garamond", Georgia, serif;
  font-weight: 600; font-size: clamp(0.975rem, 3.6vw, 1.125rem);
  line-height: 1.7;
  text-transform: uppercase; letter-spacing: 0.36em;
  color: var(--text);
  text-shadow: var(--overlay-shadow);
}
.details-events {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.55em;
}
.details-events li {
  display: flex; flex-direction: column; align-items: center; gap: 0.1em;
}
.details-ceremony {
  font-family: "EB Garamond", Georgia, serif;
  font-weight: 500; font-size: clamp(1.8rem, 6.5vw, 2.2rem);
  color: var(--text);
  text-shadow: var(--overlay-shadow);
}
.details-when {
  font-family: "EB Garamond", Georgia, serif;
  font-style: italic; font-weight: 400; font-size: clamp(1.05rem, 3.6vw, 1.2rem);
  color: #FFF6E8;
  text-shadow: var(--overlay-shadow);
}

/* Per-section caption — shown only while the dam-reservoir.png leg (index 5,
   see DAM_LEG in road.js) is on screen, independent of the title/.details
   handoff which is already resolved by then. Same top-anchored, car-clearing
   layout as .details, right down to reusing its .details-events/li markup
   for the one event row — a bare pair of <p> tags here read visibly
   cramped and inconsistent against the Vizag panel's proper row spacing. */
.dam-caption {
  position: fixed; inset: 0; z-index: 21;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  padding-top: 16vh;
  text-align: center; pointer-events: none;
  opacity: 0;
  transition: opacity .6s ease;
}
.dam-caption p { margin: 0; }

/* ------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  .car-drop, .cue span { animation: none !important; }
  .streaks { display: none !important; }
  .title, .details, .dam-caption, .venue { transition: none; }
}
