/* ===== Reset ===== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  overflow-x: clip;
}
img, svg { max-width: 100%; }
img { height: auto; display: block; }
ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0 0 1em; }
h1, h2, h3 { margin: 0; font-weight: 400; }
a { color: inherit; }

.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.sr-only,
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 12px;
  top: -48px;
  background: var(--ink);
  color: var(--paper);
  padding: 10px 16px;
  border-radius: 4px;
  z-index: 100;
  transition: top 0.2s var(--ease-gentle);
  font-family: var(--font-body);
  font-size: 14px;
}
.skip-link:focus { top: 12px; }

:focus-visible {
  outline: 2.5px solid var(--ink);
  outline-offset: 3px;
}

/* ===== Icons ===== */
.icon {
  width: 26px;
  height: 26px;
  color: var(--ink);
  flex-shrink: 0;
}

/* ===== Reveal-on-scroll (progressive enhancement; visible by default) ===== */
.reveal { opacity: 1; transform: none; }
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.55s var(--ease-gentle), transform 0.55s var(--ease-gentle);
}
.js-reveal .reveal.is-visible {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal {
    transition: none;
    opacity: 1;
    transform: none;
  }
}

/* =========================================================================
   STORY WRAPPER + SINGLE CONTINUOUS ROUTE
   One absolutely-positioned SVG overlay spans the whole main element. Its
   path/dots are generated by js/route.js from the real screen position of
   every [data-route-anchor] element, so mobile and desktop automatically
   get different (correct) geometry from the same markup and script — there
   is no hardcoded per-breakpoint path data anywhere.
   ========================================================================= */
.story {
  position: relative;
  isolation: isolate;
}

.route {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 1;
}

.route__path {
  fill: none;
  stroke: #252525;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.route__dot {
  fill: var(--paper);
  stroke: #252525;
  stroke-width: 1.7;
  vector-effect: non-scaling-stroke;
}

/* Applied by js/route.js to whichever dot's source anchor carries
   data-route-dot-filled — currently just the venue node. */
.route__dot--filled {
  fill: #252525;
}

.story > section {
  position: relative;
  z-index: 2;
}

/* Exactly one of the two route groups is visible at a time. Default
   (desktop-first here since .mobile-route is the one being toggled) shows
   the desktop route; the mobile breakpoint swaps which group paints. */
.mobile-route { display: none; }
@media (max-width: 860px) {
  .desktop-route { display: none; }
  .mobile-route { display: block; }
}

/* Invisible markers used only to compute route coordinates; the visible
   dot is drawn in the SVG, never in HTML. */
.route-anchor {
  display: block;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

/* ===== Hero ===== */
.hero {
  position: relative;
  padding: 40px var(--mobile-padding) 32px;
  text-align: center;
  max-width: var(--content-width);
  margin: 0 auto;
}

.hero__art { display: flex; justify-content: center; }
.hero__couple {
  width: min(80vw, 340px);
  height: auto;
}

/* Anchor placed at the couple's actual joined-hand point in the artwork,
   measured against the source image (see README for how this was found). */
.hero-couple {
  position: relative;
  --hands-x: 49%;
  --hands-y: 36%;
}
.route-anchor--hands {
  position: absolute;
  left: var(--hands-x);
  top: var(--hands-y);
  transform: translate(-50%, -50%);
}

/* Desktop-only element (see data-route-desktop-only in index.html; the
   mobile route never reads it). Desktop overrides this to
   position:absolute with left/top set dynamically by js/route.js from
   the couple wrapper's own live rect, not from this base rule. */
.hero__route-end {
  display: block;
}

.hero__content { position: relative; margin-top: 24px; }

/* Mobile-route starting node: sits in normal flow at the very top of
   .hero__content (so its Y tracks the couple illustration's rendered
   height instead of a guessed pixel offset — see route-anchor above),
   nudged left of the headline's own left edge so the route clears the
   lettering by a visible margin, then curves back to the shared 24px
   gutter over the next segment. Only .mobile-route is ever painted with
   this point (see the breakpoint toggle above); on desktop this element
   is absolutely positioned and 1px square, so it has no layout effect. */
.route-start-mobile {
  position: absolute;
  left: -16px;
  top: 0;
}

.hero__title-art {
  display: block;
  width: 100%;
  max-width: 400px;
  height: auto;
  margin: 0 auto;
  object-fit: contain;
}

.rings-divider { margin: 14px 0 18px; display: flex; align-items: center; justify-content: center; gap: 14px; }
.rings-divider__rule { flex: 0 1 64px; height: 1px; background: var(--line); opacity: 0.55; }
.icon--rings-divider { width: 40px; height: 24px; color: var(--line); flex-shrink: 0; }

.hero__names {
  font-family: var(--font-script);
  font-weight: 700;
  font-size: clamp(2.2rem, 9vw, 2.8rem);
  margin: 0 0 6px;
}

.hero__date {
  font-family: var(--font-date);
  font-style: italic;
  font-size: 1.3rem;
  color: var(--charcoal);
  margin: 0 0 22px;
}

/* ===== Countdown ===== */
.countdown {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.countdown__label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--charcoal);
  margin: 0;
}

.countdown__grid {
  display: grid;
  grid-template-columns:
    minmax(0, max-content)
    auto
    minmax(0, max-content)
    auto
    minmax(0, max-content)
    auto
    minmax(0, max-content);
  align-items: baseline;
  justify-content: center;
  gap: clamp(4px, 2vw, 8px);
}

.countdown__item {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  white-space: nowrap;
}

.countdown__value {
  display: inline-block;
  min-width: 2ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
}

.countdown__unit-label {
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--charcoal);
  white-space: nowrap;
}

.countdown__separator { color: var(--warm-stone); font-size: 1.1rem; }

.countdown__message {
  font-family: var(--font-script);
  font-size: 1.8rem;
  font-weight: 700;
  margin: 0;
}

.countdown__noscript {
  font-family: var(--font-date);
  font-style: italic;
  font-size: 1.15rem;
  color: var(--charcoal);
  margin: 4px 0 0;
}

@media (max-width: 359px) {
  .countdown__grid {
    grid-template-columns: repeat(2, auto);
    justify-content: center;
    row-gap: 6px;
    column-gap: 20px;
  }
  .countdown__separator { display: none; }
}

/* ===== Sections (shared) ===== */
.section { padding: var(--section-space-mobile) 0 0; }

/* ===== Split rows: welcome/city, venue/illustration, dresscode/details =====
   Mobile: plain stacked block with the shared left-edge gutter + a -26px
   anchor trick. Desktop: becomes a 3-column grid (side-a | anchor | side-b)
   so the anchor sits exactly on the shared route gutter/spine. */
.split-row {
  position: relative;
  padding-left: calc(var(--line-x) + 26px);
  padding-right: var(--mobile-padding);
}
/* .split-row__node is a direct child of the padded .split-row element
   itself (not nested inside a normal-flow child, unlike .timeline__node),
   so its "left" offset is relative to .split-row's own border edge — use
   --line-x directly rather than the "-26px from a padded child" trick. */
.split-row__node {
  position: absolute;
  left: var(--line-x);
  top: 0.5em;
}
.split-row__side--b { margin-top: 26px; }

/* Venue-only nudge: pushes the venue section's node further down than the
   welcome/dresscode ones that share .split-row__node. Desktop value is
   set in responsive.css. */
.split-row__node--venue {
  margin-top: 10px;
}

.programme__heading { padding-left: calc(var(--line-x) + 26px); padding-right: var(--mobile-padding); }

.section__heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 7vw, 2.3rem);
  margin-bottom: 18px;
}

.section__text {
  font-size: 1rem;
  color: var(--ink);
  max-width: var(--reading-width);
}

/* ===== Programme timeline ===== */
.timeline {
  position: relative;
  margin-top: 24px;
  padding-left: calc(var(--line-x) + 26px);
  padding-right: var(--mobile-padding);
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.timeline__item {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.timeline__node {
  position: absolute;
  left: -26px;
  top: 0.4em;
}

.timeline__meta { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.timeline__icon { width: 30px; height: 30px; flex-shrink: 0; }

.timeline__time {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.timeline__title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  margin: 0 0 4px;
}

.timeline__desc { font-size: 0.94rem; color: var(--charcoal); margin: 0; max-width: 46ch; }

/* ===== Venue ===== */
.venue__name {
  font-family: var(--font-script);
  font-weight: 700;
  font-size: 1.7rem;
  margin: 0 0 6px;
}
.venue__address {
  font-family: var(--font-date);
  font-style: italic;
  font-size: 1.05rem;
  color: var(--charcoal);
  margin: 0 0 18px;
}
.venue__art img,
.welcome__art img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  object-fit: contain;
}
.venue__art img { border-radius: 4px; }

/* ===== Buttons ===== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 12px 22px;
  border-radius: 8px;
  text-decoration: none;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  border: 1.5px solid var(--ink);
}
.btn--primary {
  background: var(--ink);
  color: var(--paper);
}
.btn--primary .icon { color: var(--paper); width: 20px; height: 20px; }
.btn--primary:hover, .btn--primary:focus-visible { background: #000; }

/* ===== Dress code ===== */
.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 20px;
}
.swatches__color {
  display: block;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(17, 17, 17, 0.15);
}

/* ===== Details / moments ===== */
.moments { display: flex; flex-direction: column; gap: 28px; margin-top: 24px; }

.moments__item { position: relative; display: flex; gap: 16px; align-items: flex-start; }
.moments__node {
  position: absolute;
  left: -26px;
  top: 0.5em;
}
.moments__icon { width: 32px; height: 32px; margin-top: -1px; flex-shrink: 0; }
.moments__title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1.05rem;
  margin: 0 0 6px;
}
.moments__desc { font-size: 0.94rem; color: var(--charcoal); margin: 0; max-width: 48ch; }

/* #details is itself a normal-flow child of the padded .split-row (same
   nesting level .moments__item sits at), so the same -26px trick applies. */
#details { position: relative; }
.details__route-end {
  position: absolute;
  left: -26px;
  bottom: 2px;
}

/* ===== Closing ===== */
.closing {
  text-align: center;
  padding: 32px var(--mobile-padding) 10px;
  max-width: var(--content-width);
  margin: 0 auto;
  justify-items: center;
}
.closing-rings { position: relative; display: flex; justify-content: center; width: fit-content; }

.icon--rings-closing { width: 56px; height: 32px; color: var(--line); display: block; }
.closing__lead {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 6vw, 1.9rem);
  max-width: 22ch;
  margin: 18px auto 22px;
}
/* Mobile only: the route's curve now flows into the rings themselves, so
   it needs a bit more clearance above this text than desktop's unrelated
   top-centre ending does. Desktop keeps the base 18px above (unscoped,
   untouched). */
@media (max-width: 860px) {
  .closing__lead { margin-top: 28px; }
}
.closing__rule {
  display: block;
  width: 220px;
  max-width: 60vw;
  height: 1px;
  margin: 0 auto 20px;
  background: var(--line);
  opacity: 0.55;
}
.closing__names {
  font-family: var(--font-script);
  font-weight: 700;
  font-size: 2rem;
  margin: 0 0 4px;
}
.closing__date {
  font-family: var(--font-date);
  font-style: italic;
  font-size: 1.15rem;
  color: var(--charcoal);
  margin: 0;
}

/* Small decorative postscript below the closing text. Bottom-right via
   margin-left:auto (classic auto-margin push, works regardless of the
   parent's own alignment rules) plus justify-self:end as a belt-and-
   braces match for browsers that resolve block-level alignment through
   .closing's own justify-items instead. Sized well under .closing__lead
   (~1.9rem) and .closing__names (2rem) so it stays a quiet aside, never
   competing with the actual text above it. */
.closing__postscript {
  display: block;
  width: 145px;
  max-width: 100%;
  height: auto;
  justify-self: end;
}
