/* April Royale · vowstory Utsav */

/* ═══ the four families, served by us ═══
   Self-hosted since the 11 Sep 2026 redesign; index.html no longer links
   fonts.googleapis.com. A guest opening an invitation never agreed to
   anything, and that stylesheet made their browser announce itself to a third
   party before the first paint. Five files, 112 KB, in assets/fonts/.

   ⚠️ `font-display: swap` on every face: the page paints in the fallback at
   once and reflows when the woff2 lands. `block` would hold the text
   invisible for up to three seconds, and this page opens on the couple's own
   names.

   ⚠️ Every family named anywhere in this file must appear here. Forum, Pinyon
   Script, Cardo and Marcellus are the complete set; a fifth named in a rule
   below would fall back to a system face with nothing to warn anybody. */
@font-face {
  font-family: "Forum";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/forum-latin-400-normal.woff2) format("woff2");
}
@font-face {
  font-family: "Pinyon Script";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/pinyon-script-latin-400-normal.woff2) format("woff2");
}
@font-face {
  font-family: "Cardo";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/cardo-latin-400-normal.woff2) format("woff2");
}
@font-face {
  font-family: "Cardo";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/cardo-latin-400-italic.woff2) format("woff2");
}
@font-face {
  font-family: "Marcellus";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/marcellus-latin-400-normal.woff2) format("woff2");
}

:root {
  --wine: #4e0e23;
  --wine-d: #360817;
  --wine-l: #661430;
  --gold: #c9a227;
  --gold-lt: #e7c766;
  --gold-pale: #f2dfa6;
  --gold-dim: rgba(201, 162, 39, 0.34);
  --cream: #fbf3e3;
  --cream-2: #f3e6cc;
  --ink: #4a2a18;
  --ink-2: #7a5a42;
  --ink-3: #a0866b;
  --col: 480px;
  --pad: 24px;
  --gap: 78px;
  --es: cubic-bezier(0.22, 0.61, 0.36, 1);
  --eo: cubic-bezier(0.16, 1, 0.3, 1);
}
* {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  background: var(--wine-d);
  color: var(--ink);
  font-family: "Cardo", Georgia, serif;
  font-size: 17px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}
img {
  max-width: 100%;
  display: block;
}
sup {
  font-size: 0.56em;
  vertical-align: 0.5em;
}
body:not(.open) {
  overflow: hidden;
  height: 100%;
}

#page {
  max-width: var(--col);
  margin: 0 auto;
  background: var(--cream);
  overflow: hidden;
  /* One above .edge, so the printed margins can never cover the page. */
  position: relative;
  z-index: 2;
  box-shadow: 0 0 90px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 1s var(--es);
}
body.open #page {
  opacity: 1;
}

/* ═══ COVER — opening sequence ═══ */
#cover {
  position: fixed;
  inset: 0;
  z-index: 100;
  max-width: var(--col);
  margin: 0 auto;
  background: var(--wine-d);
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    opacity 0.7s ease,
    transform 1.1s var(--eo);
}
.cv-panel {
  position: absolute;
  top: 0;
  height: 100%;
  width: 50.5%;
  overflow: hidden;
  transition: transform 1.25s var(--eo);
  will-change: transform;
}
/* layered ground: warm bloom, fine damask lattice, deep vignette */
.cv-panel::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100vw;
  background:
    radial-gradient(
      ellipse 62% 44% at 50% 42%,
      rgba(150, 34, 68, 0.55) 0%,
      rgba(78, 14, 35, 0) 70%
    ),
    repeating-linear-gradient(
      45deg,
      rgba(201, 162, 39, 0.055) 0 2px,
      rgba(201, 162, 39, 0) 2px 13px
    ),
    repeating-linear-gradient(
      -45deg,
      rgba(201, 162, 39, 0.055) 0 2px,
      rgba(201, 162, 39, 0) 2px 13px
    ),
    radial-gradient(ellipse 112% 78% at 50% 50%, #5b1029 0%, #310714 76%);
}
.cv-l {
  left: 0;
}
.cv-r {
  right: 0;
}
.cv-l::before {
  left: 0;
}
.cv-r::before {
  right: 0;
}
.cv-edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  z-index: 2;
  background: linear-gradient(
    180deg,
    transparent,
    var(--gold-pale) 22%,
    var(--gold-pale) 78%,
    transparent
  );
  opacity: 0.7;
}
.cv-l .cv-edge {
  right: 0;
}
.cv-r .cv-edge {
  left: 0;
}

.cv-dust {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}
.cv-dust i {
  position: absolute;
  border-radius: 50%;
  background: var(--gold-pale);
  filter: blur(0.4px);
  animation: rise linear infinite;
  opacity: 0;
}
@keyframes rise {
  0% {
    transform: translateY(0) scale(0.7);
    opacity: 0;
  }
  12% {
    opacity: 0.85;
  }
  85% {
    opacity: 0.5;
  }
  100% {
    transform: translateY(-92vh) scale(1.15);
    opacity: 0;
  }
}

.cv-stage {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition:
    opacity 0.55s ease,
    transform 1s var(--eo);
}

/* a slow halo behind everything */
.cv-halo {
  position: absolute;
  width: min(430px, 104%);
  aspect-ratio: 1;
  opacity: 0.42;
  animation: haloSpin 68s linear infinite;
}
@keyframes haloSpin {
  to {
    transform: rotate(360deg);
  }
}
.cv-halo circle {
  fill: none;
  stroke: var(--gold);
}

/* the arch draws itself */
.cv-arch {
  position: absolute;
  width: min(300px, 74%);
  height: auto;
  opacity: 0.95;
  pointer-events: none;
}
.cv-draw {
  stroke-dasharray: 1100;
  stroke-dashoffset: 1100;
  animation: draw 2.1s var(--eo) 0.25s forwards;
}
.cv-draw2 {
  animation-delay: 0.6s;
}
@keyframes draw {
  to {
    stroke-dashoffset: 0;
  }
}

.cv-corner {
  position: absolute;
  width: 26px;
  height: 26px;
  opacity: 0;
  animation: fadeUp 0.9s var(--eo) 1.5s forwards;
}
.cv-corner::before,
.cv-corner::after {
  content: "";
  position: absolute;
  background: var(--gold);
}
.cv-corner::before {
  width: 100%;
  height: 1px;
}
.cv-corner::after {
  width: 1px;
  height: 100%;
}
.c-tl {
  top: 8%;
  left: 11%;
}
.c-tl::before {
  top: 0;
  left: 0;
}
.c-tl::after {
  top: 0;
  left: 0;
}
.c-tr {
  top: 8%;
  right: 11%;
}
.c-tr::before {
  top: 0;
  right: 0;
}
.c-tr::after {
  top: 0;
  right: 0;
}
.c-bl {
  bottom: 8%;
  left: 11%;
}
.c-bl::before {
  bottom: 0;
  left: 0;
}
.c-bl::after {
  bottom: 0;
  left: 0;
}
.c-br {
  bottom: 8%;
  right: 11%;
}
.c-br::before {
  bottom: 0;
  right: 0;
}
.c-br::after {
  bottom: 0;
  right: 0;
}

/* a lotus opens, petal by petal */
.cv-lotus {
  width: 64px;
  height: 42px;
  margin-bottom: 20px;
}
.cv-lotus path {
  fill: none;
  stroke: var(--gold-lt);
  stroke-width: 1.2;
  stroke-linejoin: round;
  opacity: 0;
  animation: petal 0.75s var(--eo) forwards;
}
.cv-lotus path:nth-child(1) {
  animation-delay: 1.05s;
}
.cv-lotus path:nth-child(2) {
  animation-delay: 1.18s;
}
.cv-lotus path:nth-child(3) {
  animation-delay: 1.31s;
}
.cv-lotus path:nth-child(4) {
  animation-delay: 1.44s;
}
.cv-lotus path:nth-child(5) {
  animation-delay: 1.57s;
}
@keyframes petal {
  from {
    opacity: 0;
    transform: translateY(7px);
  }
  to {
    opacity: 0.95;
    transform: none;
  }
}
@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(9px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.cv-orn {
  display: block;
  width: 0;
  height: 1px;
  margin: 0;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
  animation: ornIn 1.1s var(--eo) 1.75s forwards;
}
@keyframes ornIn {
  to {
    width: min(190px, 50%);
  }
}

.cv-tap {
  position: absolute;
  bottom: min(13vh, 104px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  opacity: 0;
  animation: tapIn 1.1s var(--eo) 2.05s forwards;
}
@keyframes tapIn {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.cv-ring {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: 1px solid rgba(242, 223, 166, 0.55);
  position: relative;
  animation: breathe 3s ease-in-out infinite;
}
.cv-ring::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(201, 162, 39, 0.5);
  animation: halo 3s ease-out infinite;
}
@keyframes breathe {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(0.9);
  }
}
@keyframes halo {
  0% {
    transform: scale(1);
    opacity: 0.75;
  }
  70%,
  100% {
    transform: scale(2);
    opacity: 0;
  }
}
.cv-word {
  font-family: "Marcellus", serif;
  font-size: 12px;
  letter-spacing: 0.5em;
  text-transform: uppercase;
  text-indent: 0.5em;
  color: var(--gold-pale);
}

/* the blade of light down the seam, then the panels part */
#flash {
  position: fixed;
  inset: 0;
  z-index: 99;
  pointer-events: none;
  opacity: 0;
  max-width: var(--col);
  margin: 0 auto;
}
#flash.run {
  opacity: 1;
}
#flash .ray {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  transform: translateX(-50%) scaleY(0);
  background: linear-gradient(180deg, transparent, var(--gold-pale), transparent);
  box-shadow: 0 0 30px 7px rgba(242, 223, 166, 0.5);
}
#flash.run .ray {
  animation: rayUp 0.6s var(--eo) forwards;
}
@keyframes rayUp {
  0% {
    transform: translateX(-50%) scaleY(0);
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
  100% {
    transform: translateX(-50%) scaleY(1);
    opacity: 0;
  }
}
#flash .bloom {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 180vmax;
  height: 180vmax;
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0);
  background: radial-gradient(
    circle,
    rgba(242, 223, 166, 0.3) 0%,
    rgba(201, 162, 39, 0.12) 32%,
    rgba(78, 14, 35, 0) 62%
  );
}
#flash.run .bloom {
  animation: bloom 1.4s var(--eo) forwards;
}
@keyframes bloom {
  0% {
    transform: translate(-50%, -50%) scale(0);
    opacity: 0;
  }
  22% {
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 0;
  }
}

body.opening #cover {
  transform: scale(1.06);
  opacity: 0;
}
body.opening .cv-l {
  transform: translateX(-102%) rotate(-1.6deg);
}
body.opening .cv-r {
  transform: translateX(102%) rotate(1.6deg);
}
body.opening .cv-stage {
  opacity: 0;
  transform: translateY(-24px) scale(0.94);
}
body.opened #cover {
  display: none;
}

/* ═══ ambient float ═══ */
/* ⚠️ z-index 6, raised from 2 in the 11 Sep 2026 redesign. The printed border
   edges added then sit at 1 and #page at 2, so motes at 2 were painted level
   with the page content and lost against it. Still pointer-events:none. */
.amb {
  position: fixed;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease;
}
body.open .amb {
  opacity: 1;
}
.amb i {
  position: absolute;
  display: block;
  will-change: transform;
}
.amb .mote {
  border-radius: 50%;
  background: radial-gradient(circle, #fff3d2 0%, #e7c766 42%, rgba(201, 162, 39, 0) 72%);
  animation: rise2 linear infinite;
}
@keyframes rise2 {
  0% {
    transform: translate3d(0, 12vh, 0) scale(0.6);
    opacity: 0;
  }
  14% {
    opacity: var(--o);
  }
  86% {
    opacity: calc(var(--o) * 0.55);
  }
  100% {
    transform: translate3d(var(--dx), -108vh, 0) scale(1.1);
    opacity: 0;
  }
}
.amb .petal {
  border-radius: 60% 40% 62% 38%/50% 60% 40% 50%;
  background: linear-gradient(140deg, #f2dfa6, #c9a227);
  animation: fall linear infinite;
}
@keyframes fall {
  0% {
    transform: translate3d(0, -14vh, 0) rotate(0deg);
    opacity: 0;
  }
  12% {
    opacity: var(--o);
  }
  88% {
    opacity: calc(var(--o) * 0.5);
  }
  100% {
    transform: translate3d(var(--dx), 112vh, 0) rotate(var(--rot));
    opacity: 0;
  }
}

/* ═══ music ═══ */
.music {
  position: fixed;
  top: 18px;
  z-index: 90;
  right: max(16px, calc(50vw - var(--col) / 2 + 16px));
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(54, 8, 23, 0.72);
  border: 1px solid var(--gold-dim);
  backdrop-filter: blur(8px);
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  color: var(--gold-pale);
  transition: transform 0.3s var(--es);
}
body.open .music {
  display: flex;
  animation: fadein 1s 0.9s backwards;
}
.music:hover {
  transform: scale(1.08);
}
.music svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
  stroke: currentColor;
  position: relative;
  z-index: 2;
}
.music .ic-off {
  display: none;
}
.music[aria-pressed="false"] .ic-on {
  display: none;
}
.music[aria-pressed="false"] .ic-off {
  display: block;
}
.mu-ring {
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 1px solid var(--gold);
  opacity: 0;
}
.music[aria-pressed="true"] .mu-ring {
  animation: pulse 2.6s ease-out infinite;
}
@keyframes pulse {
  0% {
    opacity: 0.6;
    transform: scale(1);
  }
  70%,
  100% {
    opacity: 0;
    transform: scale(1.5);
  }
}
@keyframes fadein {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* ═══ HERO ═══ */
.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  background: var(--wine);
}
.hero-art {
  position: absolute;
  inset: -14% 0 -14%;
  background: url(assets/hero.webp) center 44% / cover no-repeat;
  will-change: transform;
  animation: kb 16s var(--eo) forwards;
}
/* the slow zoom moves background-size, not transform, so the parallax can own
   transform outright — the two used to overwrite each other */
@keyframes kb {
  from {
    background-size: 118% auto;
  }
  to {
    background-size: 104% auto;
  }
}
/* The linear wash alone left the names sitting on the arch's cream border
   (p95 163 at 1440). The radial holds the centre down where the copy is and
   releases at the edges, so the border, lanterns and railing still read.

   ⚠️ LIGHTENED IN THE 11 SEP 2026 REDESIGN, AND THIS IS THE ONE RULE IN THIS
   FILE WHERE TASTE AND LEGIBILITY PULL AGAINST EACH OTHER. The veil that
   shipped before was so heavy that the artwork under it barely read — worst
   case mean 30.7 out of 255, which is nearly black. The redesign roughly
   halves every stop so the arch, the lanterns and the jasmine come through.

   The reason the heavy veil existed is recorded in README.md: at the ORIGINAL
   strength the copy column measured mean 41–67 with p95 up to 163, and the
   names sat on the arch's cream border at every width from 600px up. So
   lightening this is walking back toward a fault that was found by
   measurement, and it was re-measured rather than eyeballed — the numbers are
   in README.md under "Verified again". Do not lighten it further without
   measuring the same way. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(
      ellipse 98% 60% at 50% 50%,
      rgba(54, 8, 23, 0.5) 0%,
      rgba(54, 8, 23, 0.46) 48%,
      rgba(54, 8, 23, 0.28) 74%,
      rgba(54, 8, 23, 0) 94%
    ),
    linear-gradient(
      180deg,
      rgba(54, 8, 23, 0.26) 0%,
      rgba(54, 8, 23, 0.18) 26%,
      rgba(54, 8, 23, 0.18) 55%,
      rgba(54, 8, 23, 0.3) 76%,
      rgba(54, 8, 23, 0.52) 100%
    );
}
.hero-in {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 13vh 30px 17vh;
}
/* the arch interior is image-x 20-80%, which is 60-77% of the column
   depending on how `cover` lands — anything wider puts the date and venue
   on the painted border */
.hero-in > * {
  max-width: min(68%, 34rem);
  margin-left: auto;
  margin-right: auto;
}
.hero-in > .ganesha {
  max-width: none;
}
.ganesha {
  width: clamp(78px, 20vw, 118px);
  margin: 0 auto 12px;
  filter: drop-shadow(0 0 34px rgba(201, 162, 39, 0.5));
  animation: glow 5s ease-in-out infinite;
}
@keyframes glow {
  0%,
  100% {
    filter: drop-shadow(0 0 26px rgba(201, 162, 39, 0.38));
  }
  50% {
    filter: drop-shadow(0 0 46px rgba(231, 199, 102, 0.6));
  }
}
.invocation {
  margin: 0 0 18px;
  font-size: 13.5px;
  letter-spacing: 0.1em;
  color: var(--gold-pale);
}
.eyebrow {
  margin: 0 0 12px;
  font-family: "Marcellus", serif;
  font-size: 10.5px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--gold);
}
.eyebrow-lt {
  color: var(--gold-pale);
  opacity: 0.9;
}
.hero-names {
  margin: 0;
  font-family: "Forum", Georgia, serif;
  font-weight: 400;
  font-size: clamp(34px, 9.4vw, 60px);
  line-height: 1.1;
  letter-spacing: 0.02em;
}
.hero-names .ln {
  display: block;
  color: var(--gold-pale);
  text-shadow:
    0 1px 0 rgba(140, 106, 24, 0.45),
    0 0 34px rgba(201, 162, 39, 0.3);
}
/* The letters are painted, not clipped. A -webkit-background-clip:text
   parent does not paint over transformed descendants, and these letters are
   transformed as they rise in — that combination renders them invisible. */
.hero-names .ch {
  display: inline-block;
  opacity: 0;
  transform: translateY(28px) rotate(4deg);
  animation: chIn 0.9s var(--eo) forwards;
  will-change: transform, opacity;
}
.hero-names .ln.lit {
  animation: nameGlow 5s ease-in-out infinite;
}
@keyframes nameGlow {
  0%,
  100% {
    text-shadow:
      0 1px 0 rgba(140, 106, 24, 0.5),
      0 0 34px rgba(201, 162, 39, 0.36);
  }
  50% {
    text-shadow:
      0 1px 0 rgba(140, 106, 24, 0.6),
      0 0 64px rgba(231, 199, 102, 0.7),
      0 0 14px rgba(255, 244, 210, 0.6);
  }
}
@keyframes chIn {
  to {
    opacity: 1;
    transform: none;
  }
}
.hero-names em {
  display: block;
  font-family: "Pinyon Script", cursive;
  font-style: normal;
  font-size: 0.56em;
  color: var(--gold);
  margin: -4px 0 -2px;
  line-height: 1.2;
  opacity: 0;
  animation: chIn 1s var(--eo) 1.1s forwards;
}
.hair {
  display: block;
  width: min(200px, 54%);
  height: 1px;
  margin: 20px auto;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
.hero-date {
  margin: 0 0 12px;
  font-family: "Marcellus", serif;
  font-size: clamp(13px, 3.2vw, 17px);
  letter-spacing: 0.2em;
  color: var(--gold-pale);
  text-transform: uppercase;
}
.hero-venue {
  margin: 0;
  font-size: clamp(15px, 3.3vw, 18px);
  font-style: italic;
  color: #f0dfc6;
  line-height: 1.55;
}
.hero-venue span {
  font-style: normal;
  font-size: 0.82em;
  letter-spacing: 0.08em;
  color: rgba(240, 223, 198, 0.72);
}

/* the crops carry their own chain, so no drawn stem is needed */
.hang {
  position: absolute;
  top: 0;
  z-index: 3;
  pointer-events: none;
  transform-origin: 50% 0;
  animation: sway 7s ease-in-out infinite;
  opacity: 0;
  animation-fill-mode: both;
}
body.open .hang {
  animation:
    hangIn 1.1s var(--eo) forwards,
    sway 7s ease-in-out 1.1s infinite;
}
body.open .hang-b {
  animation:
    hangIn 1.1s var(--eo) 0.16s forwards,
    sway 8.4s ease-in-out 1.26s infinite;
}
body.open .hang-c {
  animation:
    hangIn 1.1s var(--eo) 0.32s forwards,
    sway 9.2s ease-in-out 1.42s infinite;
}
body.open .hang-d {
  animation:
    hangIn 1.1s var(--eo) 0.24s forwards,
    sway 9.6s ease-in-out 1.34s infinite;
}
@keyframes hangIn {
  from {
    opacity: 0;
    transform: translateY(-60px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
/* The lanterns are lit rather than merely dropped-shadowed: two stacked warm
   glows that breathe. ⚠️ `filter` is animated here and also declared on the
   element, so the keyframes must restate BOTH shadows — a filter list is
   replaced wholesale, not merged, and naming only one would drop the other
   for the whole cycle. */
.hang img {
  width: 100%;
  filter: drop-shadow(0 0 22px rgba(255, 221, 130, 0.6))
    drop-shadow(0 0 9px rgba(255, 240, 190, 0.55));
  animation: lanGlow 4.2s ease-in-out infinite;
}
@keyframes lanGlow {
  0%,
  100% {
    filter: drop-shadow(0 0 18px rgba(255, 221, 130, 0.5))
      drop-shadow(0 0 8px rgba(255, 240, 190, 0.45));
  }
  50% {
    filter: drop-shadow(0 0 34px rgba(255, 228, 150, 0.85))
      drop-shadow(0 0 14px rgba(255, 245, 205, 0.7));
  }
}
/* every chain begins at the top edge of the hero; depth comes from the
   crops' own chain lengths, not from an offset that leaves them mid-air */
.hang-a {
  left: 4%;
  width: clamp(38px, 10vw, 64px);
  margin-top: 0;
}
/* ⚠️ Mirrored since the 11 Sep 2026 redesign: hang-b now matches hang-a's
   width and inset, and hang-d matches hang-c's on the other side. The old
   arrangement was two lanterns left, one right, at three different sizes. */
.hang-b {
  right: 4%;
  width: clamp(38px, 10vw, 64px);
  margin-top: 0;
  animation-delay: -2.4s;
  animation-duration: 8.4s;
}
.hang-c {
  left: 17%;
  width: clamp(24px, 6.4vw, 42px);
  margin-top: 0;
  animation-delay: -4.1s;
  animation-duration: 9.2s;
  opacity: 0.9;
}
.hang-d {
  right: 17%;
  width: clamp(24px, 6.4vw, 42px);
  margin-top: 0;
  animation-delay: -3.2s;
  animation-duration: 9.6s;
  opacity: 0.9;
}
@media (min-width: 960px) {
  .hang-a {
    left: 11%;
  }
  .hang-b {
    right: 12%;
  }
  .hang-c {
    left: 20%;
  }
}

.cue {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 28px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  transition:
    opacity 0.5s ease,
    transform 0.5s ease;
}
.cue span:first-child {
  font-family: "Marcellus", serif;
  font-size: 10px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: rgba(242, 223, 166, 0.72);
}
.cue-line {
  width: 1px;
  height: 42px;
  background: linear-gradient(180deg, var(--gold), transparent);
  animation: drop 2.4s ease-in-out infinite;
}
@keyframes drop {
  0% {
    transform: scaleY(0.2);
    transform-origin: top;
  }
  55% {
    transform: scaleY(1);
    transform-origin: top;
  }
  56% {
    transform-origin: bottom;
  }
  100% {
    transform: scaleY(0);
    transform-origin: bottom;
  }
}
.cue.gone {
  opacity: 0;
  transform: translateY(14px);
}

/* ═══ sections ═══ */
.sec {
  position: relative;
  padding: var(--gap) var(--pad);
  overflow: hidden;
  text-align: center;
}
.sec-cream {
  background: var(--cream);
  color: var(--ink);
}
.sec-wine {
  background: var(--wine);
  color: var(--cream);
}
.sec-wine::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    ellipse 70% 50% at 50% 30%,
    rgba(201, 162, 39, 0.1),
    transparent 70%
  );
}
.inner {
  position: relative;
  z-index: 2;
  max-width: 620px;
  margin: 0 auto;
}
.h2 {
  margin: 0 0 20px;
  font-family: "Forum", Georgia, serif;
  font-weight: 400;
  font-size: clamp(28px, 6.6vw, 44px);
  line-height: 1.22;
  color: var(--wine);
}
.h2-lt {
  background: linear-gradient(
    100deg,
    #b08d2a,
    #f5e4b0 38%,
    #fff8e4 50%,
    #e7c766 66%,
    #b08d2a
  );
  background-size: 280% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: sheen 8s linear infinite;
}
.prose {
  margin: 0 auto 16px;
  max-width: 36ch;
}
.sec-wine .prose {
  color: #f0dfc6;
}
.tag {
  margin: 26px 0 0;
  font-family: "Marcellus", serif;
  font-size: clamp(12.5px, 2.5vw, 15px);
  letter-spacing: 0.26em;
  color: var(--gold);
}
.tag-sm {
  font-size: 11.5px;
  letter-spacing: 0.3em;
  margin-top: 14px;
}

/* Sections arrive from slightly below, slightly small and slightly out of
   focus. ⚠️ The blur is the addition of the 11 Sep 2026 redesign, and it must
   be cancelled on `.in` as explicitly as the transform is — a filter left at
   a non-none value keeps the element on its own compositing layer for the
   life of the page. */
.reveal {
  opacity: 0;
  transform: translateY(34px) scale(0.975);
  filter: blur(8px);
  transition:
    opacity 1.15s var(--eo),
    transform 1.15s var(--eo),
    filter 1.15s var(--eo);
}
.reveal.in {
  opacity: 1;
  transform: none;
  filter: none;
}
/* A short cascade so a section's parts do not all land on the same frame.
   ⚠️ `:nth-child` counts EVERY sibling, not just the revealed ones, so which
   parts get a delay depends on the markup around them. That is deliberate
   here — the effect is a slight stagger, and being off by one costs 60ms of
   nothing. Do not read it as "the second reveal in the section". */
.sec .reveal:nth-child(2).in {
  transition-delay: 0.05s;
}
.sec .reveal:nth-child(3).in {
  transition-delay: 0.11s;
}
.sec .reveal:nth-child(4).in {
  transition-delay: 0.17s;
}

.deco {
  position: absolute;
  z-index: 1;
  pointer-events: none;
}
.deco-garland {
  width: min(420px, 86%);
  left: 50%;
  transform: translateX(-50%);
  top: -6px;
  opacity: 0.5;
}
.deco-lantern-g {
  width: clamp(44px, 11vw, 78px);
  right: 12px;
  top: 0;
  opacity: 0.72;
  animation: sway 6s ease-in-out infinite;
  transform-origin: 50% 0;
}
.deco-cand {
  width: clamp(34px, 9vw, 50px);
  left: -6px;
  bottom: 0;
  opacity: 0.4;
}
@keyframes sway {
  0%,
  100% {
    transform: rotate(-2.5deg);
  }
  50% {
    transform: rotate(2.5deg);
  }
}

/* ═══ invitation — the family page, on Hero_2 ═══
   ⚠️ THIS WAS A THREE-SLICE PLATE UNTIL THE 11 SEP 2026 REDESIGN, and the
   change is worth understanding before anybody reverses it. Hero_2 used to be
   cut into three: the crown with its painted lanterns, a 24px strip of flat
   cream that tiled to whatever height the family lines needed, and the base.
   The plate therefore stretched to any number of lines without ever being
   scaled or cropped, and README.md records the cuts (45% / 48–50% / 54%,
   chosen so the painted lanterns hanging to y≈43% were never sliced) and the
   measurement that made the repeat invisible (row spread 1.52).

   It is now Hero_2 whole, placed with `cover`, under a cream radial. That
   trades the plate's guarantee for the artwork's own composition: `cover`
   crops rather than stretches, so a very long family list crops more of the
   sides instead of growing a seamless middle. The three slice files are
   deleted — nothing else referenced them.

   ⚠️ The veil is CREAM here and WINE on the hero, because the copy is dark on
   this page and light on that one. `isolation: isolate` keeps the veil's
   stacking context inside the section rather than competing with #page. */
.sec-invite {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: url(assets/hero2.webp) center/cover no-repeat;
  padding: clamp(74px, 17vw, 140px) var(--pad);
}
/* ⚠️ THIS VEIL IS STRONGER THAN THE ONE THE REDESIGN SHIPPED, AND THE NUMBERS
   ARE THE REASON. It arrived as `ellipse 90% 74% at 50% 47%` with stops
   .86 / .60 / .18, which is beautiful and does not hold the copy.
   Measured behind each line with the text hidden, worst-case WCAG contrast:

       shipped   .86/.60/.18  74%   390: 4.72   834: 3.99   1440: 5.24
       this      .93/.78/.30  88%   390: 5.20   834: 4.82   1440: 5.43

   Small body text needs 4.5:1, so the family names and the "Hosted by" line
   failed at 834px and were marginal at 390px. The three-slice plate this
   replaced measured 5.07:1 for the same line, so the shipped veil was a real
   regression rather than a pre-existing weakness.

   ⚠️ The failure is at the BOTTOM of the column, not the middle: Hero_2
   darkens toward its foot, and the copy now runs over it instead of over a
   flat cream strip. Enlarging the ellipse alone does almost nothing (measured
   at 88%, 96% and 108% — 834px moved 3.99 → 4.34). What fixes it is the peak
   opacity. Both were changed: taller so the whole column is covered, and
   denser so the composite is light enough where the artwork is darkest.
   It still falls to zero at 92%, so the painted border and its flowers read
   exactly as intended.

   Re-measure the same way before lightening this. */
.fam-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(
    ellipse 92% 88% at 50% 50%,
    rgba(251, 243, 227, 0.93) 0%,
    rgba(251, 243, 227, 0.78) 46%,
    rgba(251, 243, 227, 0.3) 78%,
    rgba(251, 243, 227, 0) 92%
  );
}
/* The painted border and its flowers own the edges, so the copy is capped
   rather than padded — a max-width holds the same column whatever the
   section's own padding resolves to. */
.fam-in {
  position: relative;
  z-index: 1;
  max-width: min(90%, 30rem);
  margin: 0 auto;
  text-align: center;
}
/* in normal flow, hanging from the section's own top edge — an absolutely
   positioned ornament here sat straight on top of the heading */
.sec-count {
  padding-top: 0;
}
.chand {
  display: block;
  width: clamp(84px, 22vw, 140px);
  margin: 0 auto calc(var(--gap) * 0.42);
  animation: sway 9s ease-in-out infinite;
  transform-origin: 50% 0;
}
/* Half again as large as it was (was min(126px, 32%)). The couple's monogram
   is the first thing on this page and the plate around it is gone, so it had
   room it was not using. */
.seal {
  width: min(154px, 40%);
  margin: 0 auto 22px;
  border-radius: 50%;
  box-shadow:
    0 0 0 1px rgba(201, 162, 39, 0.4),
    0 0 0 6px var(--cream),
    0 0 0 7px rgba(201, 162, 39, 0.22);
}
.weds {
  margin: 36px 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  font-family: "Forum", Georgia, serif;
  font-size: clamp(20px, 4.6vw, 27px);
  line-height: 1.28;
  color: var(--wine);
}
.weds em {
  font-family: "Pinyon Script", cursive;
  font-style: normal;
  font-size: 1.05em;
  color: var(--gold);
  display: flex;
  align-items: center;
  gap: 14px;
  line-height: 1;
}
.weds em::before,
.weds em::after {
  content: "";
  width: 34px;
  height: 1px;
  background: var(--gold-dim);
}
.side {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.side-name {
  margin: 0 0 9px;
  line-height: 1.2;
}
.side-role {
  margin: 0 0 6px;
  font-family: "Marcellus", serif;
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold);
}
.side-parents {
  margin: 0;
  font-family: "Cardo", serif;
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 26ch;
}

/* ═══ countdown ═══ */
.clock {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  max-width: 400px;
  margin: 28px auto 0;
}
.unit {
  padding: 20px 4px 15px;
  background: rgba(251, 243, 227, 0.06);
  border: 1px solid var(--gold-dim);
  border-radius: 2px;
}
.unit b {
  display: block;
  font-family: "Forum", Georgia, serif;
  font-weight: 400;
  font-size: clamp(26px, 7vw, 38px);
  color: var(--gold-pale);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.unit i {
  display: block;
  margin-top: 10px;
  font-style: normal;
  font-family: "Marcellus", serif;
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(242, 223, 166, 0.6);
}

/* ═══ events rail ═══ */
.sec-events {
  padding-bottom: calc(var(--gap) + 10px);
}
.rail {
  position: relative;
  margin-top: 36px;
}
.rail-line {
  position: absolute;
  left: 18px;
  top: 0;
  width: 1px;
  height: 0;
  background: linear-gradient(180deg, transparent, var(--gold), var(--gold), transparent);
  transition: height 1.4s var(--eo);
}
.ev {
  position: relative;
  padding-left: 52px;
  margin-bottom: 38px;
  text-align: left;
}
.ev:last-child {
  margin-bottom: 0;
}
.ev-dot {
  position: absolute;
  left: 12px;
  top: 12px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--cream);
  border: 1px solid var(--gold);
  box-shadow: 0 0 0 4px var(--cream);
}
.ev-dot::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--gold);
}
.ev-art {
  margin: 0 0 16px;
  overflow: hidden;
  border: 1px solid var(--gold-dim);
  border-radius: 999px 999px 4px 4px/26% 26% 4px 4px;
}
.ev-art img {
  width: 100%;
  transition: transform 1.4s var(--eo);
}
.ev:hover .ev-art img {
  transform: scale(1.04);
}
.ev-name {
  margin: 0 0 8px;
  font-family: "Forum", Georgia, serif;
  font-weight: 400;
  font-size: clamp(26px, 6vw, 34px);
  color: var(--wine);
  line-height: 1;
}
.ev-when {
  margin: 0 0 2px;
  font-family: "Marcellus", serif;
  font-size: clamp(12px, 2.8vw, 14.5px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
}
.ev-time {
  margin: 0 0 12px;
  font-style: italic;
  color: var(--ink-2);
}
.ev-venue {
  margin: 0 0 16px;
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.ev-venue span {
  font-size: 0.86em;
  color: var(--ink-3);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border: 1px solid var(--gold);
  font-family: "Marcellus", serif;
  font-size: 10.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--wine);
  text-decoration: none;
  background: transparent;
  border-radius: 2px;
  transition:
    background 0.35s ease,
    color 0.35s ease;
}
.btn:hover {
  background: var(--wine);
  color: var(--gold-pale);
  border-color: var(--wine);
}

/* ═══ gallery ═══ */
.sec-gal {
  padding-left: 0;
  padding-right: 0;
}
.sec-gal .inner {
  padding: 0 var(--pad);
}
.car {
  margin-top: 30px;
  overflow: hidden;
}
.car-track {
  display: flex;
  gap: 14px;
  padding: 0 var(--pad);
  will-change: transform;
}
.ph {
  margin: 0;
  flex: 0 0 clamp(210px, 62vw, 260px);
  position: relative;
  border: 1px solid var(--gold-dim);
  background: var(--cream);
  padding: 14px;
  border-radius: 2px;
}
.ph img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
}
.ph-tag {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  font-family: "Marcellus", serif;
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--wine);
  background: var(--gold-pale);
  padding: 4px 11px;
  white-space: nowrap;
  border-radius: 1px;
}
.ph-wide {
  flex: 0 0 clamp(240px, 72vw, 320px);
  background: rgba(251, 243, 227, 0.05);
  border-style: dashed;
}
.ph-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  aspect-ratio: 1;
  text-align: center;
  padding: 16px;
}
.ph-empty b {
  font-family: "Marcellus", serif;
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.ph-empty svg {
  width: 34px;
  height: 34px;
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.1;
  opacity: 0.6;
}
.dots {
  display: flex;
  justify-content: center;
  gap: 7px;
  margin-top: 20px;
}
.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  padding: 0;
  border: 1px solid var(--gold);
  background: transparent;
  cursor: pointer;
  transition: background 0.3s;
}
.dot[aria-current="true"] {
  background: var(--gold);
}

/* ═══ venue ═══ */
.mandap {
  width: min(300px, 80%);
  margin: 8px auto 18px;
}
.v-name {
  margin: 0 0 6px;
  font-family: "Marcellus", serif;
  font-size: clamp(15px, 3.3vw, 19px);
  color: var(--wine);
  letter-spacing: 0.04em;
}
.v-addr {
  margin: 0 0 24px;
  color: var(--ink-2);
}
.mapcard {
  display: flex;
  align-items: center;
  gap: 16px;
  text-decoration: none;
  color: var(--ink);
  padding: 18px 20px;
  background: rgba(78, 14, 35, 0.05);
  border: 1px solid var(--gold-dim);
  max-width: 460px;
  margin: 0 auto;
  text-align: left;
  border-radius: 2px;
  transition:
    border-color 0.3s,
    transform 0.3s var(--es);
}
.mapcard:hover {
  border-color: var(--gold);
  transform: translateY(-2px);
}
.mc-pin,
.mc-go {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gold);
}
.mc-pin svg {
  width: 26px;
  height: 26px;
  stroke: currentColor;
  fill: none;
}
.mc-go svg {
  width: 17px;
  height: 17px;
  stroke: currentColor;
  fill: none;
}
.mc-txt {
  flex: 1;
  display: flex;
  flex-direction: column;
  font-size: 14px;
  color: var(--ink-2);
  line-height: 1.5;
}
.mc-txt b {
  font-family: "Marcellus", serif;
  font-weight: 400;
  font-size: 12.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--wine);
  margin-bottom: 3px;
}

/* ═══ RSVP (logic + flow per reference; restyled to April Royale) ═══ */
.rv-lead {
  font-family: "Cardo", serif;
  font-style: italic;
  font-size: 16px;
  color: #ebd9be;
  text-align: center;
  max-width: 300px;
  margin: 0 auto 30px;
  line-height: 1.75;
}
.rv-choices {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 400px;
  margin: 0 auto 26px;
}
.rv-ch {
  border-radius: 14px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--gold-dim);
  cursor: pointer;
  background: rgba(251, 243, 227, 0.05);
  backdrop-filter: blur(8px);
  transition:
    border-color 0.3s,
    background 0.3s,
    transform 0.2s var(--es);
}
.rv-ch:active {
  transform: scale(0.98);
}
.rv-ch.sy {
  border-color: var(--gold);
  background: rgba(201, 162, 39, 0.13);
}
.rv-ch.sn {
  border-color: #c4736a;
  background: rgba(196, 115, 106, 0.1);
}
.rv-ico {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(201, 162, 39, 0.1);
  border: 1px solid var(--gold-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    background 0.3s,
    border-color 0.3s;
}
.rv-ico svg {
  width: 21px;
  height: 21px;
  stroke: rgba(242, 223, 166, 0.65);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 0.3s;
}
.rv-ch.sy .rv-ico {
  background: rgba(201, 162, 39, 0.2);
  border-color: var(--gold);
}
.rv-ch.sy .rv-ico svg {
  stroke: var(--gold-pale);
}
.rv-ch.sn .rv-ico {
  background: rgba(196, 115, 106, 0.16);
  border-color: #c4736a;
}
.rv-ch.sn .rv-ico svg {
  stroke: #e0a79e;
}
.rv-lbl {
  font-family: "Forum", Georgia, serif;
  font-style: italic;
  font-size: 19px;
  color: #f0dfc6;
  transition: color 0.3s;
}
.rv-ch.sy .rv-lbl {
  color: var(--gold-pale);
}
.rv-ch.sn .rv-lbl {
  color: #e0a79e;
}

.rv-form {
  max-width: 400px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 15px;
  opacity: 0.3;
  pointer-events: none;
  transition: opacity 0.55s ease;
}
.rv-form.act {
  opacity: 1;
  pointer-events: all;
}
.f-lbl {
  font-family: "Marcellus", serif;
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(242, 223, 166, 0.75);
  display: block;
  margin-bottom: 8px;
  text-align: left;
}
.f-in,
.f-ta {
  width: 100%;
  background: rgba(251, 243, 227, 0.07);
  border: 1px solid var(--gold-dim);
  border-radius: 10px;
  padding: 13px 15px;
  font-family: "Cardo", serif;
  font-size: 16px;
  color: var(--cream);
  outline: none;
  transition:
    border-color 0.25s,
    box-shadow 0.25s;
  -webkit-appearance: none;
  appearance: none;
}
.f-in::placeholder,
.f-ta::placeholder {
  color: rgba(242, 223, 166, 0.35);
}
.f-in:focus,
.f-ta:focus {
  border-color: var(--gold);
  box-shadow: 0 0 0 3px rgba(201, 162, 39, 0.14);
}
.f-ta {
  resize: none;
  min-height: 88px;
}
.ff.hide {
  display: none;
}
.btn-sub {
  width: 100%;
  padding: 16px;
  background: linear-gradient(135deg, var(--gold), #a6811c);
  color: var(--wine-d);
  border: none;
  border-radius: 50px;
  font-family: "Marcellus", serif;
  font-size: 10px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
  transition:
    transform 0.2s var(--es),
    opacity 0.2s;
  margin-top: 4px;
}
.btn-sub:active {
  transform: scale(0.97);
}
.btn-sub:disabled {
  opacity: 0.6;
  pointer-events: none;
}

.rv-contact {
  max-width: 400px;
  margin: 28px auto 0;
  text-align: center;
  padding: 22px;
  background: rgba(251, 243, 227, 0.04);
  border: 1px solid var(--gold-dim);
  border-radius: 14px;
}
.rc-lbl {
  font-family: "Marcellus", serif;
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(242, 223, 166, 0.5);
  margin: 0 0 8px;
}
.rc-name {
  font-family: "Forum", Georgia, serif;
  font-style: italic;
  font-size: 19px;
  color: var(--cream);
  margin: 0 0 6px;
}
.rc-ph {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: "Marcellus", serif;
  font-size: 13.5px;
  color: var(--gold-pale);
  text-decoration: none;
  letter-spacing: 0.06em;
}
.rc-ph svg {
  width: 15px;
  height: 15px;
  stroke: var(--gold);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
}

.rv-ok {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
  padding: 40px 20px;
  max-width: 400px;
  margin: 0 auto;
}
.rv-ok.show {
  display: flex;
}

/* The refused-reply line under the form (pack 0011 T8) — the form's body
   size, in a deep wine so it reads as a warning without a new colour token. */
.rv-err {
  margin: 12px 0 0;
  font-size: 14px;
  line-height: 1.5;
  text-align: center;
  color: #7c3040;
}
.ok-r {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--gold), #8c6a18);
  border: 1px solid var(--gold-dim);
  display: flex;
  align-items: center;
  justify-content: center;
}
.ok-r svg {
  width: 28px;
  height: 28px;
  stroke: var(--wine-d);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
}
.ok-t {
  font-family: "Forum", Georgia, serif;
  font-style: italic;
  font-size: 30px;
  color: var(--gold-pale);
  margin: 0;
}
.ok-x {
  font-family: "Cardo", serif;
  font-size: 15px;
  color: #e4d2b6;
  line-height: 1.75;
  margin: 0;
}

/* ═══ footer ═══ */
.foot {
  background: var(--wine-d);
  padding: var(--gap) var(--pad) 34px;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.foot-orn {
  width: min(210px, 54%);
  margin: 0 auto 26px;
  opacity: 0.85;
}
.foot-line {
  margin: 0 0 6px;
  font-style: italic;
  color: rgba(240, 223, 198, 0.7);
}
.foot-names {
  margin: 0;
  font-family: "Forum", Georgia, serif;
  font-size: clamp(28px, 6.6vw, 40px);
  color: var(--gold-pale);
}
.ig {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 20px;
  text-decoration: none;
  color: rgba(240, 223, 198, 0.66);
  font-size: 14px;
  transition: color 0.3s;
}
.ig:hover {
  color: var(--gold-pale);
}
.ig svg {
  width: 19px;
  height: 19px;
  stroke: currentColor;
  fill: currentColor;
}
.foot-rule {
  display: block;
  width: min(180px, 50%);
  height: 1px;
  margin: 44px auto 26px;
  background: linear-gradient(90deg, transparent, var(--gold-dim), transparent);
}
.made {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  opacity: 0.8;
  transition: opacity 0.3s;
}
.made:hover {
  opacity: 1;
}
.made span {
  font-family: "Marcellus", serif;
  font-size: 9.5px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: rgba(240, 223, 198, 0.55);
}
.made img {
  width: 126px;
}

/* ═══ scale up ═══ */
@media (min-width: 600px) {
  :root {
    --col: 640px;
    --pad: 42px;
    --gap: 96px;
  }
  body {
    font-size: 18px;
  }
  .hero-in {
    padding: 14vh 56px 16vh;
  }
  .weds {
    flex-direction: row;
    align-items: flex-start;
    justify-content: center;
    gap: 24px;
  }
  .weds .side {
    flex: 1;
    max-width: 270px;
  }
  .weds em {
    margin-top: 10px;
    flex: none;
  }
  .car-track {
    gap: 18px;
  }
}
@media (min-width: 960px) {
  :root {
    --col: 840px;
    --pad: 64px;
    --gap: 116px;
  }
  body {
    font-size: 19px;
  }
  .music {
    width: 48px;
    height: 48px;
    top: 24px;
  }
  .rail-line {
    left: 50%;
    transform: translateX(-50%);
  }
  .ev {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    align-items: center;
    padding-left: 0;
    margin-bottom: 52px;
  }
  .ev-dot {
    left: 50%;
    transform: translateX(-50%);
    top: 50%;
    margin-top: -6px;
  }
  .ev-art {
    margin: 0;
  }
  .ev .ev-body {
    padding-left: 34px;
  }
  .ev-flip .ev-art {
    order: 2;
  }
  .ev-flip .ev-body {
    order: 1;
    text-align: right;
    padding-left: 0;
    padding-right: 34px;
  }
  .ph {
    flex-basis: 300px;
  }
  .ph-wide {
    flex-basis: 360px;
  }
}
@media (min-width: 1400px) {
  :root {
    --col: 920px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .reveal {
    opacity: 1;
    transform: none;
    filter: none;
  }
  .amb {
    display: none;
  }
  .hero-art {
    transform: none;
    background-size: cover;
  }
}
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* ═══ printed borders down the margins ═══
   #page is capped at --col, so a wide screen leaves two empty columns either
   side of it. A paper invitation would have a printed border there. Shown
   only from 1180px up, where those columns exist.

   ⚠️ BACKGROUNDS ON EMPTY DIVS, NOT <img> TAGS. The redesign used
   <img class="edge"> hidden with `display: none` below 1180px, and a hidden
   image is still FETCHED by every browser — 744 KB downloaded on every phone,
   which never shows them. A background-image inside a media query is fetched
   only when the query matches.

   ⚠️ The geometry is the same as the <img> version at every width: the source
   is 271 x 887, so `background-size: contain` in a 22vw x 100vh box resolves
   exactly as `height: 100vh; width: auto; max-width: 22vw` with
   `object-fit: contain` did.

   ⚠️ position: fixed at z-index 1, one below #page, pointer-events off.
   Nothing on the page can fall behind them and nothing they overlap can
   become unclickable. */
.edge {
  display: none;
  position: fixed;
  top: 0;
  height: 100vh;
  width: 22vw;
  max-width: 22vw;
  opacity: 0.92;
  pointer-events: none;
  z-index: 1;
  background-repeat: no-repeat;
  background-size: contain;
}
.edge-l {
  left: 0;
  background-position: left center;
}
.edge-r {
  right: 0;
  background-position: right center;
}
@media (min-width: 1180px) {
  .edge {
    display: block;
  }
  .edge-l {
    background-image: url(assets/border-l.webp);
  }
  .edge-r {
    background-image: url(assets/border-r.webp);
  }
}

/* ═══ the gallery photo frame ═══
   Each carousel photo is matted into a painted frame. ⚠️ The frame is an
   ::after over the photo, not a border: it is artwork with corner detail,
   which no border can draw. The photo is inset to the frame's own aperture —
   the numbers are asymmetric because the painted mount is, and they were read
   off the artwork rather than chosen. The square aspect-ratio is what lets
   one frame fit them all.
   ⚠️ .ph-wide is the dashed "your photo here" slot and is excluded on
   purpose: an empty slot inside a painted frame reads as a mistake. */
.ph:not(.ph-wide) {
  position: relative;
  aspect-ratio: 1;
  border: none;
  background: transparent;
  overflow: visible;
}
.ph:not(.ph-wide) img {
  position: absolute;
  inset: 13.5% 10.5% 15% 11.5%;
  width: auto;
  height: auto;
  aspect-ratio: auto;
  object-fit: cover;
  border-radius: 2px;
}
.ph:not(.ph-wide)::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url(assets/couple-frame.webp) center/100% 100% no-repeat;
  pointer-events: none;
  z-index: 3;
  filter: drop-shadow(0 8px 20px rgba(78, 14, 35, 0.22));
}
/* Above the frame, or the caption prints under the mount. */
.ph:not(.ph-wide) .ph-tag {
  z-index: 4;
}

/* The vowstory mark in the footer is a round badge now, so it needs a width
   of its own; the old wordmark sized itself. */
.made img {
  width: 88px;
  height: auto;
}

/* ═══ the venue route ═══
   Decorative only — see the comment on .route in index.html. The SVG's own
   overflow stays visible so the pulsing ring around the destination pin is
   not clipped by the viewBox. */
.route {
  margin: 26px auto 2px;
  width: min(330px, 86%);
}
.route-svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

/* ═══ config-driven additions ═══
   Everything below exists because the page is painted from
   script#wedding-config: elements the couple left empty are hidden rather
   than deleted, and a few slots have no counterpart in the shipped demo. */

/* Outranks the flex/grid/inline-flex display rules on .rc-ph, .mapcard,
   .ig, .btn and the name spans, so `hidden` always wins. */
[hidden] {
  display: none !important;
}

/* The deity when the couple's blessing has no artwork of its own, set in the
   display face over the hero's wine wash. */
.glyph {
  margin: 0 auto 12px;
  font-family: "Forum", Georgia, serif;
  line-height: 1;
  font-size: clamp(44px, 11vw, 68px);
  color: var(--gold-pale);
  text-shadow: 0 0 34px rgba(201, 162, 39, 0.5);
}

/* "Hosted by …" — for when an uncle or a brother is the official host.
   ⚠️ Rebuilt as a three-part block in the 11 Sep 2026 redesign: a short gold
   rule, a small-caps label, then the names. It used to be a single Marcellus
   line above the two families, at the same weight as everything around it,
   and it disappeared into the family copy. The label and the names now differ
   in family, size and colour, so the eye reads it as a note under the two
   families rather than as a third family. */
.hosted {
  margin: 28px auto 0;
  text-align: center;
  max-width: 440px;
}
.hosted-rule {
  display: block;
  width: 44px;
  height: 1px;
  margin: 0 auto 13px;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
.hosted-label {
  margin: 0 0 6px;
  font-family: "Marcellus", serif;
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--wine);
}
.hosted-names {
  margin: 0;
  font-family: "Cardo", serif;
  font-size: 15px;
  line-height: 1.62;
  color: var(--ink);
}

/* Dress code and note, two labelled cells under an event's venue.
   ⚠️ Was one italic run-on line ("Dress code: Sage & blush · High tea all
   evening") until the 11 Sep 2026 redesign.
   ⚠️ The columns are generated per cell, NOT declared as `1fr 1fr`. Most
   couples fill in one of the two, and a declared pair of tracks stays open
   whether or not anything is in it — the lone cell would sit at half width
   beside a hole. `grid-auto-flow: column` with `grid-auto-columns: 1fr` makes
   one track per cell that exists.
   ⚠️ This design alternates cream and wine bands, and the events rail can sit
   in either. The .sec-wine overrides are what keep the cells legible when the
   band behind them is dark — check both if these colours ever change. */
.ev-meta {
  margin: 12px auto 16px;
  max-width: 300px;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 9px;
}
.ev-meta > div {
  background: rgba(78, 14, 35, 0.05);
  border: 1px solid var(--gold-dim);
  padding: 7px 11px;
  min-width: 0;
}
.ev-meta dt {
  font-family: "Marcellus", serif;
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wine-l);
}
.ev-meta dd {
  margin: 3px 0 0;
  font-family: "Cardo", serif;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.4;
}
.sec-wine .ev-meta > div {
  background: rgba(255, 255, 255, 0.06);
}
.sec-wine .ev-meta dt {
  color: var(--gold-lt);
}
.sec-wine .ev-meta dd {
  color: var(--cream-2);
}

/* "(Optional)", beside a field label. */
.f-opt {
  font-size: 0.8em;
  opacity: 0.5;
}

/* A second query contact, when the couple gives one. */
.rc-ph + .rc-name {
  margin-top: 18px;
}

/* The page reads as alternating cream and wine bands. When a couple leaves a
   section empty its neighbours can end up the same tone — a photoless gallery
   puts the events and the venue together — so a hairline marks the join and
   the rhythm still reads as deliberate. script.js sets the class. */
.sec.tone-join::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  z-index: 3;
  transform: translateX(-50%);
  width: min(180px, 50%);
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--gold-dim), transparent);
}
