:root {
  --sage: #f0e4e8;
  --blush: #f7dae6;
  --ink: #3a2c31;
  --accent: #d48496;
  --band: #f3dde4;
  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Figtree", system-ui, sans-serif;
  --pad: clamp(1rem, 4vw, 2rem);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top: env(safe-area-inset-top, 0px);
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* Paints into iOS Safari status bar / Dynamic Island overscroll */
  background-color: var(--sage);
}

body {
  margin: 0;
  min-height: 100dvh;
  min-height: 100svh;
  font-family: var(--font-body);
  color: var(--ink);
  background-color: var(--sage);
  background-image:
    radial-gradient(120% 80% at 80% 0%, #fce4ee 0%, transparent 55%),
    radial-gradient(100% 70% at 0% 30%, var(--blush) 0%, transparent 50%),
    linear-gradient(165deg, var(--sage) 0%, var(--blush) 55%, #f8cfe0 100%);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  /* fixed is broken on iOS Safari and leaves the Dynamic Island area blank */
  background-attachment: scroll;
  overflow-x: hidden;
}

html.is-root-empty {
  background-color: #fff;
}

html.is-root-empty body {
  background-color: #fff;
  background-image: none;
}

/* Hold the page until the hero image is ready.
   Use visibility (not [hidden]/display:none) so Safari still loads the hero. */
html.is-booting,
html.is-booting body {
  overflow: hidden;
  overscroll-behavior: none;
  height: 100%;
  touch-action: none;
}

html.is-booting #announcement,
html.is-booting #coming-soon {
  visibility: hidden;
  pointer-events: none;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

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

[hidden] {
  display: none !important;
}

/* Coming soon */
.coming-soon {
  min-height: 100dvh;
  display: grid;
  place-content: center;
  text-align: center;
  padding: calc(var(--pad) + var(--safe-top)) var(--pad)
    calc(var(--pad) + var(--safe-bottom));
  animation: fade-up 0.8s ease both;
}

.coming-soon-label {
  margin: 0 0 0.5rem;
  font-family: var(--font-body);
  font-size: clamp(0.85rem, 2.8vw, 1rem);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

.coming-soon-text {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 12vw, 4.5rem);
  font-weight: 500;
  font-optical-sizing: auto;
  letter-spacing: -0.02em;
}

/* Hero — sticky scroll scrub:
   start full-bleed cover → zoom into fitted layout; details near end. */
.hero-stage {
  --hero-chrome: clamp(11.5rem, 18vh, 13.5rem);
  --hero-progress: 0;
  /* Name after the photo has shrunk enough to clear the nameplate */
  --hero-name-reveal: clamp(
    0,
    calc((var(--hero-progress) - 0.52) / 0.18),
    1
  );
  /* Name starts larger and scales down to the final size */
  --hero-name-settle: clamp(
    0,
    calc((var(--hero-progress) - 0.52) / 0.32),
    1
  );
  --hero-name-scale: calc(1.28 - 0.28 * var(--hero-name-settle));
  /* Extra air between photo and oversized name, eases out as it settles */
  --hero-name-gap: calc((1 - var(--hero-name-settle)) * 1.25rem);
  /* Middle name + birth details arrive near the end */
  --hero-details-reveal: clamp(
    0,
    calc((var(--hero-progress) - 0.84) / 0.16),
    1
  );
  --hero-hint: clamp(0, calc((0.55 - var(--hero-progress)) / 0.55), 1);
  position: relative;
  /* Tall stage: zoom/reveal, then a short hold before the gallery.
     svh (not dvh) so the mobile URL bar collapsing mid-scroll doesn't
     re-layout the stage and make the zoom jump. */
  height: 220dvh;
  height: 220svh;
  box-sizing: border-box;
}

/* Landscape needs more room under the wide photo for the nameplate */
.hero-stage.is-landscape {
  --hero-chrome: clamp(13rem, 24vh, 16rem);
  --hero-name-gap: calc((1 - var(--hero-name-settle)) * 1.6rem);
}

/* Mobile + landscape hero: longer settled hold before the gallery */
@media (max-width: 959px) {
  .hero-stage.is-landscape {
    height: 280dvh;
    height: 280svh;
  }
}

/* After the first-load zoom, stay settled (no re-scrub). Keep stage height
   stable — collapsing 220dvh→100dvh mid-scroll causes mobile jitter. */
.hero-stage.is-intro-done {
  --hero-progress: 1;
}

.hero-pin {
  position: sticky;
  top: 0;
  /* svh keeps the pin (and the hero centered in it) the same size while
     the mobile URL bar shows/hides during scrolling. */
  height: 100dvh;
  height: 100svh;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: calc(0.6rem + var(--safe-top)) clamp(0.75rem, 2.5vw, 1.5rem)
    calc(3rem + var(--safe-bottom));
  box-sizing: border-box;
  overflow: hidden;
}

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: min(100%, 26.5rem);
  margin-inline: auto;
  flex: 0 1 auto;
  max-height: calc(100dvh - 3.5rem);
  max-height: calc(100svh - 3.5rem);
  container-type: inline-size;
  container-name: hero;
  position: relative;
  z-index: 1;
}

/* Landscape: stack width follows the photo (JS sets .hero width to image width). */
.hero-stage.is-landscape .hero {
  width: min(100%, 64rem);
  max-width: min(100%, 64rem);
  align-items: stretch;
  margin-inline: auto;
}

.hero-stage.is-landscape .hero-media-slot,
.hero-stage.is-landscape .hero-media {
  width: 100%;
  max-width: 100%;
}

.hero-stage.is-landscape .hero-media img {
  width: 100%;
  height: auto;
  max-width: 100%;
  max-height: calc(100dvh - var(--hero-chrome));
  max-height: calc(100svh - var(--hero-chrome));
  object-fit: contain;
}

.hero-stage.is-landscape .hero-band {
  width: 100%;
  max-width: 100%;
  align-self: stretch;
  box-sizing: border-box;
}

.scroll-hint {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  position: absolute;
  left: 50%;
  bottom: calc(0.85rem + var(--safe-bottom));
  transform: translateX(-50%);
  color: color-mix(in srgb, var(--ink) 70%, var(--accent));
  text-decoration: none;
  z-index: 3;
  opacity: calc(var(--hero-hint) * 0.9);
  pointer-events: auto;
  animation: hint-pulse 2.2s ease-in-out infinite;
}

.scroll-hint-chevron {
  width: 0.55rem;
  height: 0.55rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}

.hero-media-slot {
  position: relative;
  width: 100%;
  min-height: 0;
  flex: 0 1 auto;
  margin-bottom: var(--hero-name-gap, 0px);
}

.hero-media {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 0;
  background: transparent;
  position: relative;
  z-index: 2;
  overflow: hidden;
}

/* Scrub uses fixed frame resize + object-fit cover (no CSS scale —
   scaling a small bitmap up causes banding / weird lines). */
.hero-media.is-scrubbing {
  position: fixed;
  margin: 0;
  max-width: none;
  z-index: 4;
}

.hero-media.is-scrubbing img {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  /* cover for the whole scrub — the end frame matches the image aspect,
     so the crop eases to zero (a mid-scrub switch to contain snaps). */
  object-fit: cover;
  object-position: center;
}

.hero-media img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: calc(100dvh - var(--hero-chrome));
  max-height: calc(100svh - var(--hero-chrome));
  object-fit: contain;
  object-position: center;
  opacity: 0;
  transition: opacity 0.7s ease;
}

.hero-media img.is-loaded {
  opacity: 1;
}

.hero-media.is-empty {
  display: none;
}

.hero-media-slot:has(.hero-media.is-empty) {
  display: none;
}

.hero-band {
  --band-pad-y: calc(1.15rem + (var(--hero-name-scale) - 1) * 0.9rem);
  width: 100%;
  flex: 0 0 auto;
  /* Even top/bottom around the name; details then extend the band below */
  padding: var(--band-pad-y) 1.15rem;
  text-align: center;
  background: color-mix(in srgb, var(--band) 82%, #fff);
  position: relative;
  z-index: 5;
  opacity: var(--hero-name-reveal);
  transform: translateY(calc((1 - var(--hero-name-reveal)) * 12px));
  will-change: opacity, transform;
}

.first-name {
  margin: 0;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 600;
  font-size: clamp(2.5rem, 16cqi, 3.75rem);
  line-height: 0.95;
  letter-spacing: -0.03em;
  overflow-wrap: anywhere;
  transform: scale(var(--hero-name-scale));
  /* Even grow into equal band padding (photo clearance is the slot gap) */
  transform-origin: center center;
  will-change: transform;
}

.middle-last {
  margin: calc(0.55rem * var(--hero-details-reveal)) 0 0;
  font-family: var(--font-body);
  font-size: clamp(1rem, 5cqi, 1.25rem);
  font-weight: 500;
  letter-spacing: 0.04em;
  opacity: var(--hero-details-reveal);
  transform: translateY(calc((1 - var(--hero-details-reveal)) * 10px));
  max-height: calc(var(--hero-details-reveal) * 4rem);
  overflow: hidden;
}

.details {
  margin-top: calc(0.85rem * var(--hero-details-reveal));
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(0.3rem * var(--hero-details-reveal))
    calc(0.5rem * var(--hero-details-reveal));
  font-size: clamp(0.88rem, 4cqi, 1.02rem);
  font-weight: 500;
  color: color-mix(in srgb, var(--ink) 82%, var(--accent));
  opacity: var(--hero-details-reveal);
  transform: translateY(calc((1 - var(--hero-details-reveal)) * 10px));
  max-height: calc(var(--hero-details-reveal) * 6rem);
  overflow: hidden;
}

.details .sep {
  color: var(--accent);
  user-select: none;
}

.details .detail-item {
  white-space: nowrap;
}

@container hero (max-width: 360px) {
  .details {
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
  }

  .details .sep {
    display: none;
  }
}

@supports not (font-size: 1cqi) {
  .first-name {
    font-size: clamp(2.5rem, 11vw, 3.75rem);
  }

  .middle-last {
    font-size: clamp(1rem, 3.5vw, 1.25rem);
  }

  .details {
    font-size: clamp(0.88rem, 3vw, 1.02rem);
  }
}

@keyframes hint-pulse {
  0%,
  100% {
    translate: 0 0;
  }
  50% {
    translate: 0 4px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-stage {
    --hero-progress: 1;
    height: auto;
    min-height: 100dvh;
  }

  .hero-pin {
    position: relative;
    overflow: visible;
  }

  .hero-media {
    position: relative !important;
    inset: auto !important;
    width: auto !important;
    height: auto !important;
  }

  .hero-band {
    opacity: 1;
    transform: none;
    will-change: auto;
  }

  .first-name {
    transform: none;
    will-change: auto;
  }

  .middle-last,
  .details {
    opacity: 1;
    transform: none;
  }

  .scroll-hint {
    animation: none;
    opacity: 0.7;
  }
}

/* Larger screens — portrait/square switch to a side rail.
   Landscape keeps the fluid stacked layout above (no breakpoint jump). */
@media (min-width: 960px) {
  /* Portrait / square: uncropped image + refined details rail */
  .hero-stage.is-portrait .hero,
  .hero-stage.is-square .hero {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: clamp(1.25rem, 2.5vw, 2.5rem);
    width: min(100%, 72rem);
    max-height: calc(100dvh - 4rem);
    max-height: calc(100svh - 4rem);
  }

  .hero-stage.is-portrait .hero-media-slot,
  .hero-stage.is-square .hero-media-slot,
  .hero-stage.is-portrait .hero-media,
  .hero-stage.is-square .hero-media {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    height: 100%;
    max-height: calc(100dvh - 4rem);
    max-height: calc(100svh - 4rem);
  }

  .hero-stage.is-portrait .hero-media img,
  .hero-stage.is-square .hero-media img {
    max-width: min(100%, 46rem);
    max-height: calc(100dvh - 4.5rem);
    max-height: calc(100svh - 4.5rem);
    width: auto;
    height: auto;
  }

  /* Same visual language as the stacked info box — just beside the photo */
  .hero-stage.is-portrait .hero-band,
  .hero-stage.is-square .hero-band {
    --rail: clamp(16.5rem, 28vw, 22rem);
    --band-pad-y: calc(1.35rem + (var(--hero-name-scale) - 1) * 0.9rem);
    flex: 0 0 var(--rail);
    width: var(--rail);
    align-self: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: var(--band-pad-y) 1.25rem;
    background: color-mix(in srgb, var(--band) 82%, #fff);
    border: 0;
  }

  .hero-stage.is-portrait .first-name,
  .hero-stage.is-square .first-name {
    font-size: clamp(2.5rem, 4.5vw, 3.5rem);
    line-height: 0.95;
    letter-spacing: -0.03em;
  }

  .hero-stage.is-portrait .middle-last,
  .hero-stage.is-square .middle-last {
    margin-top: calc(0.55rem * var(--hero-details-reveal));
    font-size: clamp(1rem, 1.4vw, 1.2rem);
    font-weight: 500;
    letter-spacing: 0.04em;
    color: inherit;
  }

  .hero-stage.is-portrait .details,
  .hero-stage.is-square .details {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: calc(0.3rem * var(--hero-details-reveal))
      calc(0.5rem * var(--hero-details-reveal));
    margin-top: calc(0.85rem * var(--hero-details-reveal));
    padding-top: 0;
    border-top: 0;
    font-size: clamp(0.88rem, 1.15vw, 1.02rem);
    font-weight: 500;
    letter-spacing: 0;
    line-height: 1.35;
    color: color-mix(in srgb, var(--ink) 82%, var(--accent));
  }

  .hero-stage.is-portrait .details .sep,
  .hero-stage.is-square .details .sep {
    display: inline;
    color: var(--accent);
  }

  .hero-stage.is-portrait .details .detail-item,
  .hero-stage.is-square .details .detail-item {
    white-space: nowrap;
  }
}

/* Mosaic gallery — full-width zebra rows */
.gallery.mosaic {
  width: 100%;
  max-width: none;
  scroll-margin-top: 0.75rem;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.gallery:empty {
  display: none;
}

.mosaic-row {
  display: grid;
  gap: 0.45rem;
  width: 100%;
  box-sizing: border-box;
  padding: 0.55rem max(0.5rem, env(safe-area-inset-left))
    0.55rem max(0.5rem, env(safe-area-inset-right));
}

.mosaic-row.stripe-a {
  background: color-mix(in srgb, var(--band) 82%, #fff);
}

.mosaic-row.stripe-b {
  background: var(--blush);
}

.mosaic-row.wide,
.mosaic-row.feature {
  grid-template-columns: 1fr;
}

.mosaic-row.duo {
  grid-template-columns: 1fr 1fr;
}

.mosaic-row.trio {
  grid-template-columns: 1fr 1fr;
}

.mosaic-item {
  width: 100%;
  margin: 0;
  line-height: 0;
  background: color-mix(in srgb, var(--ink) 6%, var(--blush));
}

.mosaic-item img {
  width: 100%;
  height: auto;
  vertical-align: middle;
}

/* Mobile: always one photo per row. */
@media (max-width: 639px) {
  .mosaic-row.duo,
  .mosaic-row.trio {
    grid-template-columns: 1fr;
  }
}

@media (min-width: 640px) {
  .mosaic-row {
    gap: 0.55rem;
    padding-top: 0.7rem;
    padding-bottom: 0.7rem;
    padding-left: max(
      calc((100% - 960px) / 2),
      1.25rem,
      env(safe-area-inset-left)
    );
    padding-right: max(
      calc((100% - 960px) / 2),
      1.25rem,
      env(safe-area-inset-right)
    );
    align-items: start;
  }

  .mosaic-row.trio {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

/* Contact */
.contact {
  text-align: center;
  padding: 2rem var(--pad);
  padding-left: max(var(--pad), env(safe-area-inset-left));
  padding-right: max(var(--pad), env(safe-area-inset-right));
  font-size: clamp(1rem, 3.6vw, 1.2rem);
  font-weight: 500;
  letter-spacing: 0.02em;
}

.contact p {
  margin: 0;
  line-height: 1.45;
}

.contact-email a {
  color: var(--accent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  padding-bottom: 0.1rem;
}

.contact-address {
  padding-bottom: calc(2.5rem + var(--safe-bottom));
}

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .hero-media img {
    opacity: 1;
  }

  .hero-stage {
    --hero-progress: 1;
  }

  .hero-media {
    position: relative !important;
    inset: auto !important;
    width: auto !important;
    height: auto !important;
  }

  .hero-band {
    opacity: 1 !important;
    transform: none !important;
  }

  .first-name {
    transform: none !important;
  }

  .middle-last,
  .details {
    opacity: 1 !important;
    transform: none !important;
  }
}
