/* =========================================================
   Tú Anh & Sỹ Cảnh — wedding invitation
   ========================================================= */
:root {
  --bg: #f4efe6;
  --paper: #fbf8f1;
  --ink: #23262e;
  --ink-2: #5d6068;
  --gold: #b08d57;
  --gold-2: #d9c39a;
  --navy: #1b1f28;
  --line: #d9cfb8;
  --wax-1: #c4514f;
  --wax-2: #8f2626;
  --wax-3: #5d1414;

  /* the whole site renders as a phone-width column, even on desktop */
  --col: min(100vw, 600px);
  --vw: calc(
    var(--col) / 100
  ); /* 1% of the column — use `N * var(--vw)` instead of Nvw */
  --edge-gap: max(16px, calc((100vw - 600px) / 2 + 16px)); /* fixed-position UI's distance from the column edge */

  --serif: "Cormorant Garamond", "Times New Roman", serif;
  --script: "Great Vibes", "Brush Script MT", cursive;
  --sans: "Be Vietnam Pro", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* paper fibre texture (SVG turbulence) */
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 .35 0 0 0 0 .25 0 0 0 0 .1 0 0 0 .10 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  background: #ddd4c3;
}
body {
  margin: 0 auto;
  max-width: 600px;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--serif);
  font-weight: 400;
  font-size: 17px;
  line-height: 1.6;
  overflow-x: hidden;
  box-shadow: 0 0 60px rgba(60, 40, 10, 0.18);
}
body.locked {
  overflow: hidden;
  height: 100dvh;
}
/* full-screen overlays stay inside the phone column too */
.envelope-screen,
.lightbox {
  left: 0;
  right: 0;
  max-width: 600px;
  margin: 0 auto;
}
img {
  display: block;
  max-width: 100%;
}
a {
  color: inherit;
}
b {
  font-weight: 600;
}

/* ---------- type helpers ---------- */
.eyebrow {
  display: block;
  margin: 0 0 10px;
  font-family: var(--sans);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--gold);
}
.eyebrow.light {
  color: var(--gold-2);
}
.h-serif {
  margin: 0;
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(28px, 6 * var(--vw), 36px);
  line-height: 1.15;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.h-script {
  margin: 0;
  font-family: var(--script);
  font-weight: 400;
  font-size: clamp(40px, 9 * var(--vw), 56px);
  line-height: 1.1;
  color: var(--gold);
}
.small-caps {
  margin: 0;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.wm {
  position: absolute;
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
  font-family: var(--script);
  color: var(--gold);
  opacity: 0.07;
  line-height: 1;
  font-size: clamp(110px, 24 * var(--vw), 160px);
}
.orn {
  width: 140px;
  height: 24px;
  color: var(--gold);
  display: block;
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 28px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition:
    background 0.3s,
    color 0.3s,
    border-color 0.3s;
}
.btn:hover {
  background: var(--ink);
  color: #fff;
}
.btn.dark {
  background: var(--ink);
  color: #fff;
}
.btn.dark:hover {
  background: var(--gold);
  border-color: var(--gold);
}
.btn.light {
  background: #fff;
  color: var(--ink);
  border-color: #fff;
}
.btn.light:hover {
  background: var(--gold-2);
  border-color: var(--gold-2);
}
.btn.ghost {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.55);
}
.btn.ghost:hover {
  background: #fff;
  color: var(--ink);
  border-color: #fff;
}
.btn[disabled] {
  opacity: 0.6;
  pointer-events: none;
}

/* ---------- reveal on scroll ---------- */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 1s cubic-bezier(0.2, 0.7, 0.2, 1) var(--delay, 0s),
    transform 1s cubic-bezier(0.2, 0.7, 0.2, 1) var(--delay, 0s);
}
.reveal.in {
  opacity: 1;
  transform: none;
}

/* =========================================================
   ENVELOPE
   ========================================================= */
.envelope-screen {
  --shade: 25, 12, 4; /* shadow tint (rgb) */

  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: #3b2314;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* wooden desk photo: covers the viewport; bump the scale (anchored to the top edge) to zoom in
   on the props if a future photo is framed too loosely */
.envelope-screen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: url("../img/bg-vertical.webp") center top / cover no-repeat;
  transform: scale(1);
  transform-origin: 50% 0;
}
/* darken the edges a touch so the envelope stays the brightest thing on the desk */
.envelope-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    90% 80% at 50% 45%,
    transparent 50%,
    rgba(20, 8, 2, 0.4) 100%
  );
}
.petals {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}

.envelope-stage {
  /* oversized on purpose: at 1.05 × the column the two END corners (the left end's lower one and
     the right end's upper one) bleed off the sides, while the far top-left and near bottom-right
     corners stay on screen — those two are what show the perspective */
  --w: calc(var(--col) * 1.05);
  --h: calc(var(--w) * 0.6649); /* = img/envelope/body.webp (1480 × 984) */
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  grid-template: 100% / 100%;
  place-items: center;
  /* The eye: fixed at the centre of the viewport, 720px back from the desk. Smaller = stronger
     "luật xa gần" (the far end shrinks more); larger = flatter. One value for the whole chain. */
  perspective: 720px;
}
.envelope-tilt,
.envelope-spin {
  transform-style: preserve-3d;
  will-change: transform;
}
.envelope {
  position: relative;
  width: var(--w);
  height: var(--h);
  perspective: 1500px;
  outline: none;
  --gs: 1;
}
.envelope:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 14px;
  border-radius: 8px;
}
.envelope::before {
  content: "";
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: -10%;
  height: 26%;
  z-index: 0;
  background: radial-gradient(
    50% 50% at 50% 50%,
    rgba(10, 4, 0, 0.6),
    transparent 70%
  );
  filter: blur(10px);
  opacity: var(--gs);
}
/* The envelope is built from photo layers (img/envelope/): the pocket (body.webp, transparent
   where the flap tucks in), the inside back panel (back.webp) and the flap front/back
   (flap.webp / flap-back.webp). All geometry below is measured from those images:
   flap tip at 69.31% of the height, seal centre at 58.94%. */
.env-back {
  position: absolute;
  /* stays just inside the body's photographed edge, except at the top: there the 1px inset left a
     see-through slit along the hinge once the flap swung open (the tilt/scale widened it to ~3px
     on screen), so the panel runs 2px past the top edge and the paper reads as continuous. */
  inset: -2px 1px 1px;
  z-index: 1;
  border-radius: 6px;
  background: #e3d7c1 url("../img/envelope/back.webp") center / 100% 100% no-repeat;
  /* contact shadow on the desk, key light top-left (matches the embossing in the photos) */
  box-shadow:
    1px 2px 2px rgba(var(--shade), 0.35),
    6px 12px 16px -4px rgba(var(--shade), 0.45),
    18px 36px 44px -12px rgba(var(--shade), 0.55);
}

/* --- the letter -----------------------------------------------------------------------------
   What is in the pocket is one sheet folded once across the middle. The packet you see is the
   sheet's BOTTOM half; its top half is folded down over it, card design outwards (.lf-outside).
   When that half swings up on the crease it uncovers the sheet's inside face — and that face is
   not a picture of the hero, it IS the hero: the page's real <header class="hero"> is moved into
   .letter-inside and laid out at its final full-screen size from the very first frame, then
   transform-scaled down to card size. Nothing ever reflows, so once the sheet has finished
   growing it already matches the page pixel for pixel and the screen is simply removed — there
   is nothing left to cross-fade. #card (the packet) and .letter (the whole sheet) are sized in
   px by layoutLetter(). --- */
.env-card {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 0;
  /* The letter gets a 3D context of its own — note NO preserve-3d here, that is the point. The
     fold is then projected with this eye, level with the crease, instead of inheriting the
     desk's 720px one, which is close enough to balloon the swinging half as it comes forward. */
  perspective: 2600px;
  perspective-origin: 50% 0;
  will-change: transform;
}
.letter {
  position: absolute;
  left: 0;
  bottom: 0; /* the sheet hangs below the packet box: its lower half IS the packet */
  width: 100%;
  transform-style: preserve-3d;
}
/* the paper. Clipped to the folded half, then grown to the whole sheet in step with the flap so
   its top edge stays tucked under the swinging half — see onFold() in js/main.js */
.letter-sheet {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 50%;
  overflow: hidden;
  background: var(--bg);
  border-radius: 3px;
  box-shadow: 0 10px 24px -8px rgba(0, 0, 0, 0.4);
}
.letter-inside {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
}
.hero.in-letter {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
}
/* the folded-down top half. Hinges on the crease (the packet's top edge) and lifts TOWARDS the
   viewer — hence -180° rather than 180°, which would swing it away behind the sheet. */
.letter-flap {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 50%;
  transform-origin: 50% 100%;
  transform: rotateX(-180deg);
  transform-style: preserve-3d;
  will-change: transform;
}
.lf-face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  border-radius: 3px;
  transform: translateZ(1px); /* off the sheet so the two never z-fight while coplanar */
}
.lf-outside {
  transform: rotateX(180deg) translateZ(1px);
  background: #fffcf5;
  padding: 4.5%;
  display: flex;
  flex-direction: column;
}
/* the inside face carries the top half of the same hero photo, at the same size and crop as the
   sheet below it, so the instant the flap lands the two read as one continuous picture */
.lf-inside {
  background: var(--bg);
}
.lf-hero {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 200%; /* = the whole sheet; the flap clips it to the top half */
}
.lf-hero img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 55% 42%;
}
.lf-veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(60, 45, 30, 0.08) 0%,
    rgba(60, 45, 30, 0) 35%,
    rgba(50, 38, 26, 0.72) 100%
  );
}
/* last beat: the desk goes to page paper, so the open letter is the only thing left on screen */
.envelope-screen::before,
.envelope-screen::after,
.petals {
  transition: opacity 0.9s ease;
}
.envelope-screen.bare {
  background: var(--bg);
}
.envelope-screen.bare::before,
.envelope-screen.bare::after,
.envelope-screen.bare .petals {
  opacity: 0;
}
.card-frame {
  position: absolute;
  inset: 2.6%;
  pointer-events: none;
  border: 1px solid rgba(176, 141, 87, 0.55);
  outline: 1px solid rgba(176, 141, 87, 0.25);
  outline-offset: 2px;
}
.card-photo {
  position: relative;
  flex: 0 0 58%;
  overflow: hidden;
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgba(176, 141, 87, 0.35);
}
.card-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 55% 42%;
}
.card-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  text-align: center;
  padding-top: 4px;
  min-height: 0;
}
.card-overline {
  font-family: var(--script);
  color: var(--gold);
  font-size: clamp(16px, 4.2 * var(--vw), 22px);
  line-height: 1.1;
}
.card-names {
  font-family: var(--script);
  font-size: clamp(22px, 6.4 * var(--vw), 34px);
  line-height: 1.1;
  color: var(--ink);
  white-space: nowrap;
}
.card-names i {
  font-style: normal;
  color: var(--gold);
  font-size: 0.85em;
  margin: 0 0.3em;
}
.card-rule {
  width: 38px;
  height: 1px;
  background: var(--gold);
  margin: 3px 0 2px;
  opacity: 0.8;
}
.card-date {
  font-family: var(--serif);
  font-size: clamp(11px, 3 * var(--vw), 14px);
  letter-spacing: 0.3em;
  color: var(--ink-2);
}

/* --- shadow cast by the closed flap onto the pocket (key light top-left → falls down-right).
   Two layers: a tight contact shadow at the paper edge + a wider, fainter ambient one.
   The wrapper is clipped to the envelope outline so nothing leaks past the rounded corners;
   offsets/blur scale with the envelope width so the penumbra looks the same at every size. --- */
.env-shadow {
  position: absolute;
  z-index: 7;
  left: 0;
  right: 0;
  top: 0;
  height: 82%;
  pointer-events: none;
  transform-origin: 50% 0;
  clip-path: inset(0 round 7px 7px 0 0);
}
.env-shadow::before,
.env-shadow::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 84.5%; /* = the flap's 69.31% */
  /* the flap outline: rounded shoulders, then straight edges down to the tip */
  clip-path: polygon(0 0, 100% 0, 92.4% 29.1%, 50% 100%, 7.6% 29.1%);
  background: rgba(var(--shade), 0.3);
}
.env-shadow::before {
  transform: translate(calc(var(--w) * 0.004), calc(var(--w) * 0.006));
  filter: blur(calc(var(--w) * 0.01));
  opacity: 0.55;
}
.env-shadow::after {
  transform: translate(calc(var(--w) * 0.008), calc(var(--w) * 0.014));
  filter: blur(calc(var(--w) * 0.03));
  opacity: 0.28;
}

/* --- pocket: one photo layer (side + bottom flaps, transparent V where the card slides out) --- */
.env-body {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: url("../img/envelope/body.webp") center / 100% 100% no-repeat;
  /* the pocket's cut edges lift slightly off the card */
  filter: drop-shadow(0 -1px 1px rgba(var(--shade), 0.22));
}
/* names printed on the front, below the seal */
.env-front {
  position: absolute;
  z-index: 6;
  left: 0;
  right: 0;
  bottom: 7.5%;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.env-front-names {
  font-family: var(--script);
  font-size: clamp(16px, 4.3 * var(--vw), 24px);
  line-height: 1.1;
  color: #5a4a2c;
}
.env-front-names i {
  font-style: normal;
  color: var(--gold);
  font-size: 0.85em;
  margin: 0 0.3em;
}
.env-front-date {
  font-family: var(--serif);
  font-size: clamp(9px, 2.4 * var(--vw), 11px);
  letter-spacing: 0.32em;
  color: #8a7550;
  margin-top: 2px;
}

/* --- top flap: two hinged segments so the paper flexes --- */
.env-flap {
  position: absolute;
  z-index: 8;
  left: 0;
  right: 0;
  top: 0;
  height: 75.51%; /* hinge → bottom of the wax seal (the tip itself is at 69.31%) */
  transform-origin: 50% 0;
  transform-style: preserve-3d;
  will-change: transform;
  --lit: 1;
  --litb: 0.8;
}
.flap-seg {
  position: absolute;
  transform-style: preserve-3d;
}
.seg-a {
  left: 0;
  right: 0;
  top: 0;
  height: 50.48%; /* hinge → 55% of the way to the tip */
}
.seg-b {
  /* the flap is 53.8% wide where it is split — a hair wider so nothing is cropped */
  left: 22.5%;
  width: 55%;
  top: calc(100% - 1px);
  height: calc(98.09% + 1px);
  transform-origin: 50% 0;
} /* 1px overlap hides the AA hairline at the seam */
.env-flap .face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  background-repeat: no-repeat;
}
.env-flap .front {
  filter: brightness(var(--lit));
  background-image: url("../img/envelope/flap.webp");
}
/* the back face's rotateX(180)/backface-visibility flip lives on this unfiltered wrapper, never on
   the same element as `filter`: Safari flattens a filtered element that also carries its own 3D
   transform, rasterizing it at its current projected (foreshortened) size instead of its upright
   one — that combination is what produced the ~1/3-scale first frame seen before this split. The
   front face has no local transform of its own, so it was never affected. */
.env-flap .face-back {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform: rotateX(180deg);
}
.env-flap .back {
  filter: brightness(var(--litb));
  /* flap-back.webp is stored upside down (tip at the top) so it reads correctly through rotateX */
  background-image: url("../img/envelope/flap-back.webp");
}
/* NOTE: seg-b is nested inside seg-a — use child combinators so A's rules never leak onto B.
   Each segment shows its slice of the same photo (A = top 50.48%, B = bottom 49.52%, which
   includes the wax seal) via background-size/position, so the paper is continuous across the
   seam. The photos carry their own alpha, so no clip-paths are needed for the outline. */
.seg-a > .front {
  background-size: 100% 198.1%;
  background-position: 0 0;
}
.seg-a > .face-back > .back {
  background-size: 100% 198.1%;
  background-position: 0 100%;
  /* once flipped the flap lies on the table: soft contact shadow (segment A only — the tip is
     coplanar with it and must not cast a crease onto it) */
  filter: brightness(var(--litb)) drop-shadow(0 5px 6px rgba(60, 40, 10, 0.28));
}
.seg-b > .front {
  background-size: 181.82% 201.94%;
  background-position: 50% 100%;
}
.seg-b > .face-back > .back {
  background-size: 181.82% 201.94%;
  background-position: 50% 0;
}
.env-hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(6vh + env(safe-area-inset-bottom));
  z-index: 3;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
  opacity: 0;
}
/* main line: script, flanked by thin rules */
.hint-title {
  display: flex;
  align-items: center;
  gap: 16px;
  font-family: var(--script);
  font-size: clamp(26px, 7 * var(--vw), 34px);
  line-height: 1;
  color: #f4e8d2;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
.hint-title::before,
.hint-title::after {
  content: "";
  width: 36px;
  height: 1px;
  background: var(--gold);
  opacity: 0.7;
}
/* subtitle: small caps with a slow pulse — the actual call to action */
.hint-sub {
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: #d9c7a5;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
  animation: hint-fade 2.6s ease-in-out infinite;
}
@keyframes hint-fade {
  0%,
  100% {
    opacity: 0.55;
  }
  50% {
    opacity: 1;
  }
}

/* =========================================================
   HERO
   ========================================================= */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  color: #fff;
  overflow: hidden;
  background: var(--bg);
}
.hero-bg,
.hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.hero-bg img {
  object-fit: cover;
  object-position: 55% 42%;
}
.hero-veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(60, 45, 30, 0.08) 0%,
    rgba(60, 45, 30, 0) 35%,
    rgba(50, 38, 26, 0.72) 100%
  );
}
.hero-inner {
  position: relative;
  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 22px 20px; /* the scroll cue below carries the rest of the bottom margin */
}
.hero-names {
  margin: 0.04em 0 12px;
  font-family: var(--script);
  font-weight: 400;
  font-size: clamp(42px, 11 * var(--vw), 80px);
  line-height: 0.98;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.3);
}
.hero-names span {
  display: block;
}
.hero-names em {
  font-style: normal;
  color: var(--gold-2);
  font-size: 0.9em;
  margin-right: 0.12em;
}
.hero-date {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 12px;
}
.hd-day {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(32px, 8.5 * var(--vw), 52px);
  line-height: 0.9;
}
.hd-dow,
.hd-month {
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  line-height: 1.6;
}
.hd-dow {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  opacity: 0.85;
}
.countdown {
  display: flex;
  gap: clamp(14px, 5 * var(--vw), 30px);
}
.countdown div {
  display: flex;
  flex-direction: column;
}
.countdown b {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(19px, 5.2 * var(--vw), 28px);
  line-height: 1;
}
.countdown small {
  font-family: var(--sans);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  opacity: 0.75;
  margin-top: 5px;
}
/* the invitation ends on a full screen of photo, so say out loud that there is more below:
   small caps under the countdown with a mote falling down a hairline */
.scroll-cue {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  padding-bottom: calc(20px + env(safe-area-inset-bottom));
  text-decoration: none;
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.78);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  transition:
    opacity 0.5s ease,
    transform 0.5s ease;
}
/* it has said its piece the moment the guest scrolls — see js/main.js */
.scroll-cue.gone {
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
}
.scroll-cue span {
  position: relative;
  width: 1px;
  height: 34px;
  overflow: hidden;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.75),
    rgba(255, 255, 255, 0)
  );
  /* the photo behind can be pale (the dress) — give the hairline its own edge */
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55));
}
.scroll-cue span::after {
  content: "";
  position: absolute;
  left: -1px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--gold-2);
  animation: scroll-mote 2.6s ease-in-out infinite;
}
@keyframes scroll-mote {
  0% {
    top: -4px;
    opacity: 0;
  }
  25%,
  70% {
    opacity: 1;
  }
  100% {
    top: 34px;
    opacity: 0;
  }
}

/* =========================================================
   BÁO TIN — photo fades into text
   ========================================================= */
.ceremony {
  position: relative;
  background: var(--bg);
  overflow: hidden;
}
.ceremony .wm {
  right: calc(-4 * var(--vw));
  top: calc(4 * var(--vw));
  transform: rotate(-6deg);
  font-size: clamp(90px, 24 * var(--vw), 160px);
}
.ceremony-photo {
  position: relative;
  /* HHN_9723 is a tall portrait (1009x1400) with the London Eye filling the top half and the
     couple filling the bottom half — there's no vertical "dead space" to crop into, so the box
     is sized to the photo's own ratio instead of a fixed viewport height. That way object-fit:
     cover has nothing left to crop and the whole frame shows. */
  aspect-ratio: 1009 / 1400;
}
.ceremony-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 58%;
  /* fade only starts below the couple's faces (~65% down) and is fully resolved to the page
     background by 95%, so the fade — and the text section overlapping it below — never lands
     on the Eye or their faces, just the dress hem at the very bottom of the frame */
  -webkit-mask-image: linear-gradient(180deg, #000 65%, transparent 95%);
  mask-image: linear-gradient(180deg, #000 65%, transparent 95%);
}
.ceremony-body {
  position: relative;
  z-index: 1;
  margin-top: -7%; /* sits inside the photo's guaranteed-transparent tail (95%-100% of its height) */
  padding: 0 22px 44px;
}
/* both families always share one row: the ornament stands vertically between them. The side
   columns are equal halves (not auto), so the ornament column sits on the exact centre of the
   page however wide the two names run; each family still hugs its outer edge. */
.families {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 8px;
  align-items: start;
  margin: 26px 0 18px;
}
.families .family {
  justify-self: start;
  max-width: 100%; /* capped to its track so an over-wide name overflows (and JS can shrink it) instead of widening the cell */
}
.families .family:last-child {
  justify-self: end;
}
.family-side {
  display: block;
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 6px;
}
.family-parents {
  margin: 0;
  /* nhà gái's two parent lines vs. nhà trai's one are both hard <br />-broken (not wrapped), so
     reserving a fixed 2-line height here — rather than sizing to content — is what keeps
     .family-addr starting at the same height under both sides */
  min-height: 2.8em; /* 2 × line-height */
  /* stays clear of the centred ornament down to a 320px screen. Android's accessibility text
     scaling multiplies px sizes too, so fitOneLine() in main.js shrinks it further on overflow */
  font-size: clamp(13px, 4.2 * var(--vw), 22px);
  line-height: 1.4;
  white-space: nowrap;
}
.family-addr {
  margin: 4px 0 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 9px;
  line-height: 1.5;
  color: var(--ink-2);
}
.orn.v {
  width: 24px;
  height: 96px;
  margin-top: 2px;
}
.couple-script {
  margin: 28px 0 16px;
  text-align: center;
  font-family: var(--script);
  font-size: clamp(44px, 11 * var(--vw), 66px);
  line-height: 1;
}
.couple-script em {
  font-style: normal;
  color: var(--gold);
  font-size: 0.7em;
  margin: 0 0.25em;
}
.couple-note {
  margin: 20px 0 0;
  text-align: center;
}

/* =========================================================
   HAI LỄ — Vu Quy / Thành Hôn
   ========================================================= */
.rites {
  position: relative;
  overflow: hidden;
  padding-bottom: clamp(56px, 8 * var(--vw), 100px);
}
.rites-photo {
  position: relative;
  height: 46vh;
  min-height: 280px;
}
.rites-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 48% 40%;
  -webkit-mask-image: linear-gradient(180deg, #000 40%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 40%, transparent 100%);
}
.rites-grid {
  position: relative;
  z-index: 1;
  margin: -14vh auto 0;
  padding: 0 22px;
  max-width: 980px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 26px;
  text-align: center;
}
.rite {
  padding: 8px 0;
}
.rite-name {
  margin: 0 0 10px;
  font-family: var(--script);
  font-weight: 400;
  color: var(--gold);
  font-size: clamp(40px, 11 * var(--vw), 56px);
  line-height: 1.1;
}
.rite-venue {
  margin: 6px 0 8px;
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(22px, 5.4 * var(--vw), 30px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.rite-addr {
  margin: 0 0 18px;
  font-style: italic;
  color: var(--ink-2);
  font-size: clamp(14px, 4.4 * var(--vw), 17px);
  line-height: 1.55;
  white-space: nowrap;
}
.rite-when {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 12px 22px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.rite-when b {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(48px, 11 * var(--vw), 64px);
  line-height: 0.9;
}
.rite-when span {
  text-align: left;
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  line-height: 1.8;
  color: var(--ink-2);
}
.rite-when em {
  font-style: normal;
  font-family: var(--serif);
  font-size: clamp(26px, 6 * var(--vw), 34px);
  color: var(--gold);
  padding-left: 14px;
  border-left: 1px solid var(--line);
}
/* =========================================================
   ALBUM — collage of prints on paper
   ========================================================= */
.paper-bg {
  background-color: var(--bg);
  background-image: var(--grain);
}
.album {
  position: relative;
  padding: clamp(56px, 9 * var(--vw), 120px) 0;
  overflow: hidden;
}
.album-head {
  position: relative;
  text-align: center;
  padding: 0 22px;
  margin-bottom: clamp(26px, 5 * var(--vw), 56px);
}
.collage {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: calc(25 * var(--vw));
  gap: 8px;
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 12px;
}
.collage a {
  position: relative;
  display: block;
  overflow: hidden;
  background: #e9e1d2;
  padding: 4px;
  box-shadow: 0 10px 24px -14px rgba(60, 40, 10, 0.45);
  background: var(--paper);
}
.collage img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 30%;
  opacity: 0;
  transition:
    opacity 0.8s,
    transform 1.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.collage img.loaded {
  opacity: 1;
}
.collage a:hover img {
  transform: scale(1.04);
}
.collage a:nth-child(1) {
  grid-column: 1 / 3;
  grid-row: 1 / 4;
}
.collage a:nth-child(2) {
  grid-column: 3 / 5;
  grid-row: 1 / 3;
}
.collage a:nth-child(3) {
  grid-column: 3 / 5;
  grid-row: 3 / 5;
}
.collage a:nth-child(4) {
  grid-column: 1 / 3;
  grid-row: 4 / 6;
}
.collage a:nth-child(5) {
  grid-column: 3 / 4;
  grid-row: 5 / 7;
}
.collage a:nth-child(6) {
  grid-column: 4 / 5;
  grid-row: 5 / 7;
}
.collage a:nth-child(7) {
  grid-column: 1 / 3;
  grid-row: 6 / 7;
}
.collage a:nth-child(8) {
  grid-column: 1 / 3;
  grid-row: 7 / 9;
}
.collage a:nth-child(9) {
  grid-column: 3 / 5;
  grid-row: 7 / 9;
}
.collage a:nth-child(10) {
  grid-column: 1 / 5;
  grid-row: 9 / 11;
}
.collage a:nth-child(11) {
  grid-column: 1 / 3;
  grid-row: 11 / 13;
}
.collage a:nth-child(12) {
  grid-column: 3 / 5;
  grid-row: 11 / 13;
}
.collage .cap {
  position: absolute;
  left: 14px;
  bottom: 12px;
  font-family: var(--script);
  font-size: 24px;
  color: #fff;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  opacity: 0.95;
}
.album-foot {
  position: relative;
  text-align: center;
  margin-top: 30px;
}

/* =========================================================
   TIỆC CƯỚI — photo bleeding into an editorial program, two
   receptions set as running text (no card boxes), calendar keepsake
   ========================================================= */
.party {
  position: relative;
  overflow: hidden;
  padding-bottom: clamp(64px, 9 * var(--vw), 110px);
}
.party-photo {
  position: relative;
  height: 42vh;
  min-height: 260px;
}
.party-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 55%;
  -webkit-mask-image: linear-gradient(180deg, #000 22%, transparent 78%);
  mask-image: linear-gradient(180deg, #000 22%, transparent 78%);
}
.party-head {
  position: relative;
  z-index: 1;
  text-align: center;
  margin: -9vh auto 0;
  padding: 0 22px;
}
.receptions {
  position: relative;
  max-width: 420px;
  margin: clamp(30px, 5 * var(--vw), 56px) auto 0;
  padding: 0 26px;
}
.reception {
  position: relative;
  text-align: center;
}
.rc-tag {
  margin: 0 0 4px;
  font-family: var(--script);
  font-weight: 400;
  font-size: clamp(32px, 8.4 * var(--vw), 42px);
  color: var(--gold);
  line-height: 1.1;
}
.party-time {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 4px 0 18px;
}
.party-time b {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(56px, 14 * var(--vw), 92px);
  line-height: 0.9;
  color: var(--ink);
}
.party-time span {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  line-height: 1.9;
  text-align: left;
  color: var(--ink-2);
}
.party-venue {
  margin: 0 auto 26px;
  max-width: 320px;
  font-size: 17px;
  line-height: 1.6;
}
.party-venue b {
  display: block;
  font-size: clamp(24px, 6 * var(--vw), 32px);
  font-weight: 500;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}
.party-venue small {
  display: block;
  margin-top: 6px;
  font-family: var(--serif);
  font-size: 14px;
  font-style: italic;
  letter-spacing: 0;
  color: var(--ink-2);
}
.party-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 24px;
}
.party-actions .btn {
  padding: 12px 20px;
}
.timeline {
  list-style: none;
  margin: 0 auto;
  max-width: 260px;
  padding: 18px 0 0;
  border-top: 1px solid var(--line);
  position: relative;
  text-align: left;
}
.timeline::before {
  content: "";
  position: absolute;
  left: 78px;
  top: 26px;
  bottom: 8px;
  width: 1px;
  background: rgba(176, 141, 87, 0.4);
}
.timeline li {
  display: flex;
  align-items: center;
  padding: 7px 0;
}
.timeline time {
  flex: 0 0 62px;
  font-family: var(--serif);
  font-size: 20px;
  color: var(--gold);
  text-align: right;
  order: 0;
}
.timeline li::before {
  content: "";
  order: 1;
  flex: 0 0 8px;
  height: 8px;
  margin: 0 12px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 4px rgba(176, 141, 87, 0.18);
}
.timeline span {
  order: 2;
}
.timeline span {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
}

.calendar-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  padding: clamp(44px, 7 * var(--vw), 80px) 22px 0;
}
.calendar {
  position: relative;
  background: var(--paper);
  color: var(--ink);
  padding: 28px 22px 20px;
  width: 100%;
  max-width: 360px;
  transform: rotate(-2deg);
  box-shadow: 0 30px 50px -22px rgba(60, 40, 10, 0.5);
  background-image: var(--grain);
}
.calendar::before {
  content: "";
  position: absolute;
  top: -13px;
  left: 50%;
  width: 96px;
  height: 26px;
  background: rgba(255, 255, 255, 0.55);
  transform: translateX(-50%) rotate(2deg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.cal-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 1px solid var(--line);
  padding-bottom: 8px;
  margin-bottom: 12px;
}
.cal-head span {
  font-family: var(--script);
  font-size: 32px;
  color: var(--gold);
  line-height: 1;
}
.cal-head em {
  font-style: normal;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.3em;
}
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px 0;
  text-align: center;
}
.cal-grid .dow {
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--ink-2);
  padding-bottom: 6px;
}
.cal-grid .dow.sun {
  color: var(--wax-2);
}
.cal-grid .day {
  font-family: var(--serif);
  font-size: 18px;
  height: 36px;
  display: grid;
  place-items: center;
  position: relative;
}
.cal-grid .day.sun {
  color: var(--wax-2);
}
.cal-grid .day.wedding {
  font-weight: 600;
  color: var(--wax-2);
}
.cal-grid .day.wedding svg {
  position: absolute;
  inset: -4px;
  width: calc(100% + 8px);
  height: calc(100% + 8px);
}
.cal-grid .day.wedding svg path {
  fill: none;
  stroke: var(--wax-2);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 200;
  stroke-dashoffset: 200;
}
.reveal.in .day.wedding svg path {
  animation: draw 1.2s ease-out 0.5s forwards;
}
.reveal.in .day.wedding:nth-of-type(2) svg path {
  animation-delay: 1.1s;
}
@keyframes draw {
  to {
    stroke-dashoffset: 0;
  }
}
.cal-note {
  margin: 14px 0 0;
  text-align: center;
  font-style: italic;
  color: var(--ink-2);
  font-size: 16px;
}

/* =========================================================
   RSVP — reply form
   ========================================================= */
.rsvp {
  position: relative;
  padding: clamp(56px, 9 * var(--vw), 120px) 22px;
}
.rsvp-head {
  text-align: center;
  margin-bottom: clamp(30px, 6 * var(--vw), 48px);
}
.rsvp-sub {
  margin: 10px auto 0;
  max-width: 340px;
  font-family: var(--serif);
  font-size: 16px;
  font-style: italic;
  color: var(--ink-2);
}
.rsvp-form {
  max-width: 380px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.rsvp-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  text-align: left;
}
.rsvp-field span {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.rsvp-field input {
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  padding: 8px 2px;
  font-family: var(--serif);
  font-size: 18px;
  color: var(--ink);
  outline: none;
  transition: border-color 0.3s;
}
.rsvp-field input:focus {
  border-color: var(--gold);
}
.rsvp-field-qty input {
  max-width: 100px;
}
.rsvp-submit {
  margin-top: 4px;
}
.rsvp-status {
  min-height: 1.2em;
  margin: 0;
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-align: center;
}
.rsvp-status.ok {
  color: #3f7d4c;
}
.rsvp-status.error {
  color: #b3453f;
}

/* both states occupy the same grid cell so the row is always sized to the taller of the
   two — swapping which one is visible never changes the section's height (no layout shift
   for the content below once the guest submits) */
.rsvp-stage {
  display: grid;
}
.rsvp-body,
.rsvp-done {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
}
.rsvp-done {
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
  max-width: 340px;
  margin: 0 auto;
  padding: 12px 0 4px;
  text-align: center;
  align-self: start;
}
.rsvp-stage.is-done .rsvp-body {
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
}
.rsvp-stage.is-done .rsvp-done {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}
.rsvp-done .orn {
  margin: 0 auto 20px;
}
.rsvp-done .h-script {
  color: var(--gold);
}
.rsvp-done-text {
  margin: 14px auto 0;
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* =========================================================
   THANKS — photo fading into paper
   ========================================================= */
.thanks {
  position: relative;
  min-height: 88svh;
  display: grid;
  place-items: end center;
  overflow: hidden;
  text-align: center;
  background: var(--bg);
}
.thanks-bg,
.thanks-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.thanks-bg img {
  object-fit: cover;
  object-position: 48% 30%;
}
.thanks-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(244, 239, 230, 0) 0%,
    rgba(244, 239, 230, 0) 35%,
    rgba(244, 239, 230, 0.92) 72%,
    var(--bg) 100%
  );
}
.thanks-inner {
  position: relative;
  padding: 0 22px calc(48px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
}
.thanks-seal {
  width: 92px;
  max-width: 30%;
  height: auto;
  margin-bottom: 18px;
  transform: rotate(-8deg);
  filter: drop-shadow(0 8px 12px rgba(60, 10, 10, 0.35));
}
.thanks-seal.reveal {
  transform: rotate(-8deg) translateY(28px);
}
.thanks-seal.reveal.in {
  transform: rotate(-8deg);
}
.thanks-text {
  margin: 0;
  font-size: clamp(18px, 4 * var(--vw), 24px);
  font-style: italic;
  line-height: 1.6;
  color: var(--ink-2);
}
.thanks-names {
  margin: 20px 0 6px;
  font-family: var(--script);
  /* sized off the usable width (column minus the 22px gutters) so the line never wraps: the
     script face renders this name ~7.2 × its font-size, /7.4 keeps a margin. JS shrinks it
     further if a fallback font loads wider — see fitOneLine() in main.js */
  font-size: min(calc((var(--col) - 44px) / 7.4), 92px);
  line-height: 1;
  white-space: nowrap;
}
.thanks-names em {
  font-style: normal;
  color: var(--gold);
  font-size: 0.6em;
  margin: 0 0.3em;
}
.thanks-date {
  margin: 0;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.4em;
  color: var(--ink-2);
}

/* =========================================================
   RSVP FAB + MUSIC + LIGHTBOX
   ========================================================= */
.rsvp-fab {
  position: fixed;
  right: calc(var(--edge-gap) + 44px + 10px); /* 44px music-btn width + 10px gap */
  bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 900;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 11px 16px;
  border-radius: 999px;
  border: 1px solid var(--gold);
  background: rgba(251, 248, 241, 0.92);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  backdrop-filter: blur(6px);
  transition:
    background 0.3s,
    color 0.3s;
}
.rsvp-fab:hover {
  background: var(--gold);
  color: #fff;
}

.music-btn {
  position: fixed;
  right: var(--edge-gap);
  bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 900;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--gold);
  background: rgba(251, 248, 241, 0.9);
  color: var(--gold);
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  backdrop-filter: blur(6px);
}
.music-btn.playing svg {
  animation: spin 3s linear infinite;
}
.music-btn:not(.playing)::after {
  content: "";
  position: absolute;
  width: 26px;
  height: 1.5px;
  background: var(--gold);
  transform: rotate(-45deg);
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 950;
  background: rgba(244, 239, 230, 0.97);
  display: grid;
  place-items: center;
  padding: 16px;
}
.lightbox[hidden],
.music-btn[hidden] {
  display: none;
}
.lightbox figure {
  margin: 0;
  text-align: center;
}
.lightbox img {
  max-width: 100%;
  max-height: 84vh;
  box-shadow: 0 24px 60px -20px rgba(60, 40, 10, 0.5);
  background: var(--paper);
  padding: 6px;
}
.lightbox figcaption {
  margin-top: 12px;
  color: var(--ink-2);
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.3em;
}
.lb-close,
.lb-nav {
  position: absolute;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  font-family: var(--serif);
  line-height: 1;
  opacity: 0.75;
  padding: 10px;
}
.lb-close {
  top: 8px;
  right: 12px;
  font-size: 40px;
}
.lb-nav {
  top: 50%;
  transform: translateY(-50%);
  font-size: 56px;
}
.lb-prev {
  left: 4px;
}
.lb-next {
  right: 4px;
}
.lb-close:hover,
.lb-nav:hover {
  opacity: 1;
}

/* ---------- fluid seal sizes (browsers without clamp()/min() keep the px fallbacks above) ---------- */
@supports (width: min(clamp(1px, 2px, 3px), 4px)) {
  .thanks-seal {
    width: clamp(80px, 22 * var(--vw), 104px);
  }
}

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .hint-sub,
  .scroll-cue span,
  .music-btn.playing svg {
    animation: none;
  }
  .env-hint {
    opacity: 1;
  }
}
