/* ==========================================================================
   SECTIONS
   ========================================================================== */

/* Fixed atmosphere canvas sits behind everything */
/* Petals fall in front of the page. Kept below the nav (600) and the music
   player (500) so nothing drifts across a control. */
#atmosphere {
  position: fixed;
  inset: 0;
  z-index: 420;
  pointer-events: none;
}

/* Smooth-scroll rig */
#viewport { position: relative; z-index: 1; }
body.is-smooth #viewport {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  will-change: transform;
}

/* ---------- HERO ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: clamp(5rem, 12vh, 9rem) var(--gutter);
  overflow: hidden;
}

/* ---- Slideshow ----
   .hero is a grid with place-items: center, and an absolutely positioned
   child inherits align-self: center — which stops it stretching to its inset
   box and shrinks it to content instead. Full-bleed layers have to opt back
   into stretching, or the photographs sit as a band in the middle. */
.hero__show {
  position: absolute;
  inset: 0;
  place-self: stretch;
  width: 100%;
  height: 100%;
  z-index: 0;
  overflow: hidden;
}

/* Scoped to the parent on purpose. These slides are also .plate elements,
   and features.css loads after this file — at equal specificity the later
   .plate { position: relative } wins, which un-stacks the slideshow and
   leaves only the first photograph on screen. */
.hero__show > .hero__slide {
  position: absolute;
  inset: 0;
  place-self: stretch;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  transition: opacity 2.4s var(--ease-silk);
}
.hero__show > .hero__slide.is-on { opacity: 1; }
.hero__show > .hero__slide.is-on > img { animation: heroZoom 12s var(--ease-glide) forwards; }
@keyframes heroZoom {
  from { transform: scale(1.02); }
  to   { transform: scale(1.13); }
}

/* No filter over the photograph. Readability is handled locally instead: a
   soft pool of page colour sitting directly behind the type and feathering
   to nothing well before the edges of the frame. */
.hero__inner {
  position: relative;
  z-index: 3;
  padding-inline: 0.5rem;
  scale: 1;
  transition: scale 2.6s var(--ease-glide);
  isolation: isolate;
}
.hero__inner::before {
  content: "";
  position: absolute;
  inset: -14% -10%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(closest-side,
    rgba(242, 235, 220, 0.82) 0%,
    rgba(242, 235, 220, 0.52) 52%,
    rgba(242, 235, 220, 0) 80%);
  filter: blur(14px);
}
body.is-loading .hero__inner { scale: 1.025; }

.hero__title {
  font-family: var(--font-script);
  /* A script carries less ink per em than Cormorant, so it needs more size,
     no negative tracking (the letters connect) and room for its descenders. */
  /* Sized almost purely off viewport width. The old clamp had a 3.4rem floor,
     which on a 360px phone is wider than the screen for a nine-letter name in
     a script face — the overflow was then cut by .hero's overflow: hidden. */
  /* --fit is measured in the browser by hero.js and only ever scales down,
     so the names can never overflow whatever the font's real metrics are. */
  font-size: calc(clamp(2.4rem, 15vw, 8rem) * var(--fit, 1));
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: 0;
  font-kerning: normal;
  margin-bottom: clamp(2rem, 4vw, 3.4rem);
  text-shadow: 0 0 22px rgba(242, 235, 220, 0.85);
}

/* The names are written on rather than pushed up. A per-letter split would
   break the kern pairs that hold a script together, so the whole line stays
   one text run and a clip-path uncovers it left to right. */
.hero__title .line {
  overflow: visible;
  text-align: center;
  padding-block: 0.16em;
  margin-block: -0.16em;
}

/* inline-block so the box hugs the text rather than filling the column —
   a full-width block box meant clip-path cut the glyphs at the container
   edge the moment a name was wider than its column. nowrap keeps a name on
   one line so it can be measured. */
.hero__title .line > span {
  display: inline-block;
  white-space: nowrap;
  transform: none;
  opacity: 1;
  /* Every inset is negative except the one doing the wipe. The font's content
     area is 1.332em against a 1.15em line box, so the ink sits 0.091em proud
     of the span on both sides — a top inset of 0 shaved the flourish off every
     capital and ascender. -20% covers it with room to spare. */
  clip-path: inset(-20% 100% -35% -12%);
  transition: clip-path 1.5s var(--ease-glide);
}

/* Held until the curtain parts — the writing is the reveal, so it must not
   happen behind a closed curtain. */
body:not(.is-loading) .hero__title.is-in .line > span {
  clip-path: inset(-20% -12% -35% -12%);
}
.hero__title.is-in .line:nth-child(1) > span { transition-delay: 0.35s; transition-duration: 1.5s; }
.hero__title.is-in .line:nth-child(2) > span { transition-delay: 1.7s;  transition-duration: 0.55s; }
.hero__title.is-in .line:nth-child(3) > span { transition-delay: 2.1s;  transition-duration: 1.5s; }

/* The ampersand stays Cormorant italic — two scripts side by side compete,
   and this is the pairing the rest of the page already uses. */
.hero__title .amp {
  display: block;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.3em;
  line-height: 1;
  margin: 0.2em 0;
  color: var(--gold);
}

.hero__venue {
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--charcoal);
  text-shadow: 0 0 14px rgba(242, 235, 220, 0.95);
  margin: 0;
}

/* One minimal button, centred under the names. */
.hero__enter {
  display: inline-flex;
  align-items: center;
  gap: 0.85em;
  margin-top: clamp(2rem, 4vw, 3rem);
  padding: 0.95em 2.4em;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(250, 245, 234, 0.55);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  color: var(--charcoal);
  font-family: var(--font-body);
  font-size: var(--fs-mono);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  text-indent: var(--track-wide);
  cursor: pointer;
  transition:
    border-color 0.5s var(--ease-silk),
    color 0.5s var(--ease-silk),
    background 0.5s var(--ease-silk);
}
.hero__enter:hover,
.hero__enter:focus-visible {
  border-color: var(--olive);
  color: var(--olive);
  background: rgba(250, 245, 234, 0.85);
}
.hero__enter:active { transform: scale(0.985); }

/* Slideshow position, 1 – 4 */
.hero__dots {
  opacity: 0;
  transition: opacity 1.1s var(--ease-silk);
  position: absolute;
  left: 50%;
  bottom: clamp(1.6rem, 4vh, 2.6rem);
  translate: -50% 0;
  z-index: 3;
  display: flex;
  gap: 0.7rem;
}
body:not(.is-loading) .hero__dots { opacity: 1; transition-delay: 4.4s; }

.hero__dot {
  width: 22px;
  height: 2px;
  padding: 0;
  border: 0;
  background: rgba(255, 255, 255, 0.55);
  box-shadow: 0 0 0 1px rgba(46, 46, 46, 0.14);
  cursor: pointer;
  transition: background 0.6s var(--ease-silk);
}
.hero__dot.is-on { background: var(--olive); }

/* ---------- DETAILS ---------- */
.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: clamp(1.4rem, 2.5vw, 2.6rem);
}
.grid-3 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: clamp(1.4rem, 2.5vw, 2.4rem);
}

/* ---------- EVENT TIMELINE ---------- */
.timeline {
  --tl-pad: clamp(3rem, 8vw, 5.5rem);
  position: relative;
  max-width: 40rem;
  margin-inline: auto;
  padding-left: var(--tl-pad);
}
.timeline__rail {
  position: absolute;
  left: calc(var(--tl-pad) / 2);
  top: 0.6em; bottom: 3em;
  width: 1px;
  background: var(--line);
}
.timeline__rail i {
  position: absolute;
  inset: 0 0 auto 0;
  height: 0%;
  background: linear-gradient(180deg, var(--gold), var(--olive));
  transition: height 0.25s linear;
}

.stop { position: relative; padding-bottom: clamp(3rem, 6vw, 5rem); }
.stop:last-child { padding-bottom: 0; }

.stop__dot {
  position: absolute;
  left: calc(-0.5 * var(--tl-pad));
  top: 0.55em;
  width: 11px; height: 11px;
  translate: -6px 0;
  border-radius: 50%;
  background: var(--beige);
  border: 1px solid var(--line);
  transition: all 0.7s var(--ease-silk);
}
.stop.is-active .stop__dot {
  background: var(--gold);
  border-color: var(--gold);
  box-shadow: 0 0 0 7px rgba(201,183,141,0.2), 0 0 22px rgba(201,183,141,0.9);
}
.stop__time {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.3rem);
  font-style: italic;
  color: var(--neptune);
  line-height: 1;
}
.stop__what {
  display: block;
  margin-top: 0.7rem;
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--ink-soft);
}
.stop__note { margin-top: 0.9rem; color: var(--ink-faint); font-size: 0.9rem; }

/* ---------- FINAL ---------- */
.final {
  position: relative;
  text-align: center;
  padding-block: clamp(4.5rem, 3rem + 7vw, 9rem);
  overflow: hidden;
}
.final__words {
  position: relative;
  z-index: 2;
  font-size: clamp(2rem, 1rem + 5vw, 5.2rem);
  line-height: 1.06;
  max-width: 20ch;
  margin-inline: auto;
}

/* ---------- FOOTER ---------- */
.footer {
  position: relative;
  z-index: 1;
  isolation: isolate;
  overflow: hidden;
  text-align: center;
  padding: clamp(3rem, 5vw, 4.5rem) var(--gutter) clamp(4rem, 7vw, 6rem);
  border-top: 1px solid var(--line);
}
.footer > *:not(.ground) { position: relative; z-index: 1; }
.footer__mark {
  font-family: var(--font-display);
  font-size: clamp(1.8rem, 1.2rem + 2.4vw, 2.8rem);
  font-weight: 300;
  letter-spacing: 0.08em;
}
.footer__date {
  margin-top: 0.8rem;
  font-size: var(--fs-mono);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--ink-faint);
}
.footer__note {
  margin-top: 2.2rem;
  color: var(--ink-soft);
  font-size: 0.9rem;
  max-width: 26rem;
  margin-inline: auto;
}
.footer__credit {
  margin-top: 2.6rem;
  font-size: 0.68rem;
  letter-spacing: var(--track-mid);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.footer__credit a { border-bottom: 1px solid var(--line-gold); }
.footer__credit a:hover { color: var(--olive); }

/* ---------- Photographic section ground ----------
   The photograph is a real element carrying an inline background-image, not a
   pseudo-element fed through a custom property. Relative url() inside a custom
   property has to survive substitution to resolve, and when it does not it
   fails silently — there is nothing to inspect and no error. A plain element
   can be seen in devtools and cannot fail that way.

   --veil controls how much beige sits over the photograph. Set it per section
   inline: lower to show more of the image, higher if the type is fighting it. */
.section--photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;   /* the drifting ground scales past its box */
}

/* Everything except the ground sits above it */
.section--photo > *:not(.ground) {
  position: relative;
  z-index: 1;
}

.ground {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  animation: groundDrift 40s var(--ease-glide) infinite alternate;
}
@keyframes groundDrift {
  from { transform: scale(1.04); }
  to   { transform: scale(1.12); }
}

.ground::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    var(--beige) 0%,
    rgba(242, 235, 220, var(--veil-edge, 0.78)) 11%,
    rgba(242, 235, 220, var(--veil, 0.58)) 50%,
    rgba(242, 235, 220, var(--veil-edge, 0.78)) 89%,
    var(--beige) 100%);
}

/* ---- Adaptive ground ----
   A flat veil is only safe when you know the photograph. Raise --veil enough
   for a dark image and a bright one turns to milk; tune it for a bright image
   and a dark one swallows the type.

   This normalises first. A sheet of paper colour in `lighten` mode replaces
   any pixel darker than the paper, so shadows lift while highlights are left
   alone — both kinds of photograph reach the veil in roughly the same range,
   and charcoal keeps its contrast either way.

   Two knobs, both settable inline:
     --lift  how hard the shadows are raised   (0 none, 1 flat paper)
     --veil  how much paper sits on top after  (0 clear, 1 opaque)

   The footer runs 0.62 / 0.20. Checked against WCAG relative luminance with
   charcoal on paper, that holds 5.6:1 on a near-black photograph and 11.6:1
   on a blown-out one — comfortably past the 4.5:1 floor at both ends, while
   leaving far more of the image visible than a flat veil could. Leaning on
   --veil alone would need about 0.75 to survive a dark photo, and that turns
   a bright one to milk. */
.ground--adaptive { isolation: isolate; }

.ground--adaptive::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--beige);
  mix-blend-mode: lighten;
  opacity: var(--lift, 0.62);
}
.ground--adaptive::after { z-index: 2; }

/* Safari before 15.4 ignores mix-blend-mode inside a filtered context; the
   flat veil alone still carries it, just with less of the photo showing. */
@supports not (mix-blend-mode: lighten) {
  /* Without the lift the veil has to do all the work on its own. */
  .ground--adaptive { --veil: 0.75; --veil-edge: 0.88; }
}

/* Cards need to lift off a photograph more than off flat paper, and the
   hairlines that read fine on plain beige get lost over one. */
.section--photo .card { border-color: var(--line-gold); }
.section--photo .timeline__rail { background: rgba(74, 62, 44, 0.3); }
.section--photo .stop__dot { border-color: rgba(74, 62, 44, 0.38); }
.section--photo .clock__unit + .clock__unit::before {
  background: linear-gradient(180deg, transparent, rgba(74, 62, 44, 0.3), transparent);
}

@media (prefers-reduced-motion: reduce) {
  .ground { animation: none; }
}

/* A full-bleed photo band sits between two sections; these pull the facing
   edges in so the photograph reads as connected to them rather than floating
   in a strip of empty page. */
.section--tight-top    { padding-top: clamp(2.5rem, 5vw, 4rem); }
.section--tight-bottom { padding-bottom: clamp(2.5rem, 5vw, 4rem); }

/* ---------- Shared: centred prose block ---------- */
.prose-center {
  max-width: var(--measure);
  margin-inline: auto;
  text-align: center;
  color: var(--ink-soft);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: 2rem;
}
.actions--center { justify-content: center; }

/* ---------- Responsive ---------- */
@media (prefers-reduced-motion: reduce) {
  /* The generic reset covers transform and opacity but not the write, so the
     names would sit permanently clipped to nothing. */
  .hero__title .line > span { clip-path: none !important; }
  .hero__dots, .player.is-ready { opacity: 1 !important; transition-delay: 0s !important; }
}

@media (max-width: 640px) {
  .hero { padding-inline: 0.9rem; }
  .hero__inner { padding-inline: 0; }

  .player { padding-right: 1rem; }
  .player__text { display: none; }

  /* Sections were stacking too tightly against the viewport edges */
  .section { padding-block: clamp(3rem, 9vw, 4.5rem); }
  .timeline { --tl-pad: 2.6rem; }
  .actions { justify-content: center; }
  .card { padding: 1.6rem 1.3rem; }
}

@media (max-width: 420px) {
  .player__disc { width: 38px; height: 38px; }
}