/* ==========================================================================
   RESET + BASE
   ========================================================================== */

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

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg-current);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  transition: background-color 600ms linear;
}

body {
  min-height: 100vh;
  position: relative;
}

img, svg, video { max-width: 100%; display: block; }
button { font: inherit; cursor: pointer; border: none; background: transparent; color: inherit; }
figure { margin: 0; }
p { margin: 0; }
h1, h2, h3 { margin: 0; font-weight: 300; }

/* ==========================================================================
   OPENING — Real 3D Frame + Brush-stroke Reveal
   ========================================================================== */

.opening {
  position: relative;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--sp-9) var(--gutter-desktop);
  overflow: hidden;
  isolation: isolate;
  perspective: 1600px;
  perspective-origin: 50% 50%;
}

@media (max-width: 1023px) { .opening { padding: var(--sp-7) var(--gutter-tablet); } }
@media (max-width: 767px)  { .opening { padding: var(--sp-7) var(--gutter-mobile); } }

/* On desktop the stage is a horizontal spread: 3D frame on the left, the
   acrostic poem + date column on the right.  This lets the whole opening
   fit inside one viewport without scrolling and gives the page an
   editorial-spread feel — image left, message right. */
.opening__stage {
  position: relative;
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  justify-content: center;
  gap: clamp(32px, 5vw, 96px);
  z-index: 2;
}

@media (max-width: 1023px) {
  .opening__stage { gap: clamp(24px, 4vw, 64px); }
}

@media (max-width: 767px) {
  .opening__stage {
    grid-template-columns: 1fr;
    gap: var(--sp-7);
  }
}

/* ===== The 3D Frame =====
   --frame-aspect is set by JS once opening.jpg loads, so the frame matches
   the photo's natural ratio and never crops it.  Width is computed as the
   smaller of (max horizontal) vs (max vertical × aspect) so the frame stays
   inside the viewport whether the photo is portrait OR landscape. */
.frame {
  --frame-max-w: 30vw;
  --frame-max-h: 58vh;
  --frame-aspect: 2 / 3;
  --frame-bezel: 24px;
  --frame-inner-pad: 8px;

  position: relative;
  width: min(var(--frame-max-w), calc(var(--frame-max-h) * var(--frame-aspect)));
  max-width: 480px;
  min-width: 260px;
  aspect-ratio: var(--frame-aspect);
  transform-style: preserve-3d;
  transform: rotateY(0deg) rotateX(0deg);
  transition: transform 320ms var(--ease-smooth);
  will-change: transform;
  cursor: crosshair;
  filter: drop-shadow(0 40px 50px rgba(0, 0, 0, 0.55))
          drop-shadow(0 16px 24px rgba(0, 0, 0, 0.42));
}

/* Outer bevel — gold ornate frame face with highlight & shadow gradients */
.frame__bevel-outer {
  position: absolute;
  inset: 0;
  border-radius: 1px;
  background:
    /* edge highlight ring at very outer */
    linear-gradient(135deg,
      rgba(255, 232, 184, 0.95) 0%,
      rgba(229, 184, 109, 0.95) 12%,
      rgba(168, 122, 60, 0.95) 28%,
      rgba(229, 184, 109, 0.95) 50%,
      rgba(108, 76, 32, 0.95)  72%,
      rgba(229, 184, 109, 0.95) 88%,
      rgba(82,  56, 18, 0.95)  100%);
  box-shadow:
    /* outer drop */
    0 0 0 1px rgba(40, 24, 6, 0.6),
    /* deep cast shadow */
    0 30px 80px -10px rgba(0, 0, 0, 0.7);
}

/* Inner bevel — sells the recess.  This is the dark inner trough between
   the gold edge and the photo cavity. */
.frame__bevel-inner {
  position: absolute;
  inset: var(--frame-bezel);
  background:
    linear-gradient(135deg,
      rgba(40, 28, 12, 0.95) 0%,
      rgba(18, 12, 6, 0.95) 50%,
      rgba(50, 36, 18, 0.95) 100%);
  box-shadow:
    /* deep inner shadow on top/left = recessed */
    inset 6px 6px 12px rgba(0, 0, 0, 0.7),
    inset -3px -3px 8px rgba(255, 220, 160, 0.08),
    /* tiny gold rim where the inner trough meets the photo */
    inset 0 0 0 1px rgba(229, 184, 109, 0.25);
}

/* Photo cavity — sits inside the inner bevel.  Holds two photo layers:
   the dim base (always faintly visible) and the bright drop-in layer that
   lands from above when the cursor enters the frame.  See .frame.is-dropped
   for the dropped state. */
.frame__photo {
  position: absolute;
  inset: calc(var(--frame-bezel) + var(--frame-inner-pad));
  overflow: hidden;
  background: #14161e;
  z-index: 1;
  transform-origin: 50% 50%;
  will-change: transform;
}

/* Both photo layers fill the cavity. JS maps data-photo="opening" to
   ./assets/photos/opening.webp; the gradient remains visible until real files exist. */
.frame__photo-layer {
  position: absolute;
  inset: 0;
  background-image:
    var(--photo-url, none),
    linear-gradient(135deg,
      rgba(229, 184, 109, 0.55) 0%,
      rgba(82, 50, 38, 0.75) 45%,
      rgba(198, 107, 92, 0.48) 100%);
  background-size: cover, cover;
  background-position: center, center;
}

.frame__photo-layer img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* DIM layer — always visible, low brightness + low saturation.
   This is the "若隐若现" base state. */
.frame__photo-layer--dim {
  z-index: 1;
  filter: brightness(0.48) saturate(0.65) contrast(0.92);
  opacity: 0.82;
}

/* BRIGHT layer — full color, drops in from above on cursor enter (Lando style).
   Animation handled in JS via GSAP for full control over the back.out bounce. */
.frame__photo-layer--bright {
  z-index: 2;
  will-change: transform, opacity;
}

/* Subtle gold "landing glow" — flares briefly when the bright layer lands */
.frame__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  background: radial-gradient(
    ellipse 60% 80% at 50% 0%,
    rgba(255, 230, 180, 0.18) 0%,
    transparent 60%
  );
  opacity: 0;
  transition: opacity 380ms ease-out;
  mix-blend-mode: screen;
}

.frame__photo.is-flash::after {
  opacity: 1;
  animation: landing-flash 700ms cubic-bezier(0.22, 1, 0.36, 1) 1;
}

@keyframes landing-flash {
  0%   { opacity: 0; transform: translateY(-20px); }
  35%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0.55; transform: translateY(0); }
}

/* Hint */
.frame__hint {
  position: absolute;
  inset: auto 0 calc(var(--frame-bezel) + var(--frame-inner-pad) + var(--sp-3)) 0;
  text-align: center;
  z-index: 3;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--accent-gold);
  pointer-events: none;
  opacity: 0;
  transition: opacity 480ms var(--ease-smooth);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
}

.frame.is-hint-visible .frame__hint { opacity: 0.85; animation: hint-pulse 2.4s ease-in-out infinite; }
.frame.is-touched .frame__hint { opacity: 0; }

@keyframes hint-pulse {
  0%, 100% { letter-spacing: 0.28em; }
  50%      { letter-spacing: 0.36em; }
}

/* Caption column: sits to the right of the 3D frame.  Hidden initially,
   fades in as the frame collapses (handled in JS). */
.opening__caption {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(16px, 2vh, 28px);
  max-width: min(46vw, 480px);
  opacity: 0;
  transform: translateY(20px);
  will-change: transform, opacity;
}

.opening__poem {
  display: flex;
  flex-direction: column;
  gap: clamp(2px, 0.5vh, 6px);
  text-align: left;
  font-variation-settings: "SOFT" 50, "WONK" 0;
}

.opening__poem-line {
  display: grid;
  grid-template-columns: clamp(26px, 3.6vh, 40px) 1fr;
  gap: clamp(6px, 0.9vh, 12px);
  align-items: baseline;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(15px, 2vh, 22px);
  line-height: 1.4;
  letter-spacing: 0;
  color: var(--text-primary);
  margin: 0;
}

/* The highlighted first letter — visibly larger + gold so the column reads
   A · T · H · E · N · A vertically. */
.acrostic {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 3.8vh, 42px);
  color: var(--accent-gold);
  line-height: 1;
  letter-spacing: -0.02em;
  text-align: left;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

.poem-text {
  display: block;
}

.opening__date {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: 0.16em;
  color: var(--text-muted);
  margin: 0;
  align-self: flex-start;
}

/* On mobile the caption goes back under the frame and centres */
@media (max-width: 767px) {
  .opening__caption { align-items: center; max-width: 100%; text-align: center; }
  .opening__date    { align-self: center; }
}

/* Scroll prompt */
.opening__scroll {
  position: absolute;
  bottom: var(--sp-7);
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--text-dim);
  letter-spacing: 0.06em;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  z-index: 3;
  opacity: 0;
}

.opening__scroll-arrow {
  font-family: var(--font-body);
  display: inline-block;
  animation: scroll-bounce 2.4s ease-in-out infinite;
}

@keyframes scroll-bounce {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50%      { transform: translateY(6px); opacity: 1; }
}

/* Mobile: scale frame to viewport width, smaller bezel */
@media (max-width: 767px) {
  .frame { --frame-max-w: 78vw; --frame-max-h: 56vh; --frame-bezel: 18px; }
  .opening__poem-line { font-size: clamp(15px, 4.2vw, 22px); grid-template-columns: clamp(26px, 8vw, 38px) 1fr; }
  .acrostic { font-size: clamp(26px, 8vw, 40px); }
}

/* ==========================================================================
   GALLERY — Horizontal pinned scroll, continuous flow of photos
   ========================================================================== */

.gallery {
  position: relative;
  height: 100vh;
  overflow: hidden;
  isolation: isolate;
}

.gallery__viewport {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}

/* The continuous stream of photos on a flat dusk-blue background.
   align-items: stretch lets per-photo align-self ("up" / "down") create
   a true skyline rhythm; the default mid-tier photos still sit centred. */
.gallery__stream {
  display: flex;
  flex-wrap: nowrap;
  align-items: stretch;
  height: 100%;
  padding: 0 calc(var(--gutter-desktop) * 1.4);
  gap: clamp(28px, 3.5vw, 64px);
  perspective: 1600px;
  will-change: transform;
  background: var(--bg-base);
}

@media (max-width: 1023px) { .gallery__stream { padding: 0 var(--gutter-tablet); } }
@media (max-width: 767px)  { .gallery__stream { padding: 0 var(--gutter-mobile); } }

/* Subtle bottom vignette to ground the row of photos */
.gallery::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  background:
    radial-gradient(ellipse at 50% 110%, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.28) 90%);
  mix-blend-mode: multiply;
}

/* ==========================================================================
   PHOTO COMPONENT — continuous-flow tile
   Each photo is a flex-row item with its own height tier (back/mid/front)
   and per-image aspect ratio (set by JS once the file loads, so nothing is
   cropped).  Photos sit on the long gradient stream like prints on a shelf.
   ========================================================================== */

.photo {
  --photo-h: 64vh;          /* mid-tier default; overridden by depth tier below */
  --photo-aspect: 3 / 4;    /* JS replaces this with each image's natural ratio */
  --photo-tilt: 0deg;

  position: relative;
  flex-shrink: 0;
  height: var(--photo-h);
  aspect-ratio: var(--photo-aspect);
  transform: rotate(var(--photo-tilt));
  transform-origin: 50% 50%;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  isolation: isolate;
  /* Default vertical placement: centre.  data-shift overrides for skyline rhythm. */
  align-self: center;
}

/* Skyline rhythm: pin some photos to top, some to bottom, some hard-shift.
   These are stronger than depth tiers — they push photos out of the centerline. */
.photo[data-shift="up"]      { align-self: flex-start; margin-top: clamp(2vh, 4vh, 6vh); }
.photo[data-shift="down"]    { align-self: flex-end;   margin-bottom: clamp(2vh, 4vh, 6vh); }
.photo[data-shift="up-far"]  { align-self: flex-start; margin-top: clamp(0vh, 1vh, 2vh); }
.photo[data-shift="down-far"]{ align-self: flex-end;   margin-bottom: clamp(0vh, 1vh, 2vh); }

.photo__inner {
  --photo-placeholder: linear-gradient(135deg,
    rgba(229, 184, 109, 0.32) 0%,
    rgba(52, 57, 74, 0.68) 46%,
    rgba(198, 107, 92, 0.28) 100%);
  --photo-url: none;

  width: 100%;
  height: 100%;
  background-image: var(--photo-url), var(--photo-placeholder);
  background-size: cover, cover;
  background-position: center, center;
  background-color: #14161e;
  border: 1px solid rgba(242, 235, 220, 0.10);
  position: relative;
  overflow: hidden;
  box-shadow:
    0 28px 60px -28px rgba(0, 0, 0, 0.78),
    0 0 0 1px rgba(255, 255, 255, 0.022);
}

.photo__inner img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Depth tiers — vary the *height* (and therefore the apparent size) so the
   row reads as a skyline rather than a flat ribbon. */
.photo--depth-front { --photo-h: 76vh; z-index: 3; }
.photo--depth-mid   { --photo-h: 60vh; z-index: 2; opacity: 0.96; }
.photo--depth-back  { --photo-h: 48vh; z-index: 1; opacity: 0.82; }

/* ==========================================================================
   HANDWRITTEN NOTES — paper-like cards pinned to specific photos
   ========================================================================== */

/* Handwritten card.  Sizes are tied to viewport-height so every card is
   visually consistent across devices, AND scales relative to the photo
   it's attached to (since photo height is itself vh-based).  Smaller back-tier
   photos still get a slightly smaller card via the photo--depth-back override. */
.handwritten {
  position: absolute;
  z-index: 5;
  background: var(--polaroid-paper, #F4EDDC);
  padding: clamp(8px, 1.2vh, 14px) clamp(10px, 1.6vh, 18px);
  box-shadow: 0 14px 28px -10px rgba(0, 0, 0, 0.55);
  transform: rotate(-3deg);
  max-width: clamp(140px, 17vh, 240px);
  pointer-events: none;
  opacity: 0;
}

/* Per-author placement: each note tucks into a different corner with a different
   tilt so adjacent cards in the row don't all tuck into the same side. */
.handwritten[data-author="Y"] { bottom: -7%; right: -10%; transform: rotate(4deg); }
.handwritten[data-author="L"] { top: -6%;    right: -8%;  transform: rotate(-5deg); }
.handwritten[data-author="N"] { bottom: -8%; left: -10%;  transform: rotate(3deg); }
.handwritten[data-author="M"] { top: -7%;    left: -9%;   transform: rotate(-6deg); }
.handwritten[data-author="J"] { bottom: -7%; right: -10%; transform: rotate(-2deg); }
.handwritten[data-author="K"] { top: 4%;     right: -12%; transform: rotate(7deg); }
.handwritten[data-author="A"] { bottom: -9%; left: -8%;   transform: rotate(-4deg); }
.handwritten[data-author="S"] { top: -6%;    right: -10%; transform: rotate(5deg); }

.handwritten p {
  font-family: var(--font-hand, "Caveat", cursive);
  font-size: clamp(15px, 1.85vh, 22px);
  line-height: 1.28;
  color: var(--accent-ink, #1A1A1A);
  font-weight: 400;
  margin: 0;
}

.handwritten__sig {
  display: block;
  margin-top: clamp(4px, 0.5vh, 8px);
  font-family: var(--font-hand, "Caveat", cursive);
  font-weight: 600;
  font-size: clamp(18px, 2.3vh, 28px);
  color: var(--accent-rust, #C66B5C);
  text-align: right;
}

/* Smaller cards on back-tier (already-small) photos so they never
   overpower the photo they're attached to. */
.photo--depth-back .handwritten {
  max-width: clamp(120px, 13vh, 180px);
}
.photo--depth-back .handwritten p   { font-size: clamp(13px, 1.55vh, 18px); }
.photo--depth-back .handwritten__sig { font-size: clamp(16px, 1.9vh, 22px); }

/* ==========================================================================
   GROUP BRIDGE
   ========================================================================== */

.group {
  position: relative;
  min-height: 110vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 4vh, 48px);
  padding: var(--sp-11) var(--gutter-desktop);
  background: var(--bg-base);
  overflow: hidden;
  isolation: isolate;
}

@media (max-width: 1023px) { .group { padding: var(--sp-9) var(--gutter-tablet); } }
@media (max-width: 767px)  { .group { padding: var(--sp-9) var(--gutter-mobile); min-height: 100vh; } }

.group__label {
  font-family: var(--font-display);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(22px, 3.4vh, 36px);
  color: var(--accent-gold);
  letter-spacing: 0.06em;
  text-align: center;
  margin: 0;
  z-index: 2;
  opacity: 0;
  transform: translateY(16px);
}

.group__photo {
  z-index: 2;
  width: min(86vw, 980px);
  height: auto !important;
  aspect-ratio: var(--photo-aspect, 16 / 10);
  opacity: 0;
  flex-shrink: initial;
}

.group__photo .photo__inner {
  height: 100%;
  aspect-ratio: var(--photo-aspect, 16 / 10);
}

/* ==========================================================================
   LETTER — single handwritten note, presented as the emotional centrepiece
   between the gallery and the group photo.  The image is the real letter
   on white paper; we frame it as a slightly tilted sheet with a soft shadow.
   ========================================================================== */

.letter {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 4vh, 48px);
  padding: var(--sp-11) var(--gutter-desktop);
  background: var(--bg-base);
  isolation: isolate;
}

@media (max-width: 1023px) { .letter { padding: var(--sp-9) var(--gutter-tablet); } }
@media (max-width: 767px)  { .letter { padding: var(--sp-9) var(--gutter-mobile); } }

.letter__label {
  font-family: var(--font-display);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(22px, 3.4vh, 36px);
  color: var(--accent-gold);
  letter-spacing: 0.06em;
  text-align: center;
  margin: 0;
  opacity: 0;
  transform: translateY(16px);
}

.letter__paper {
  position: relative;
  display: inline-block;
  background: #F4EDDC;
  padding: clamp(12px, 1.6vh, 24px);
  transform: rotate(-1.2deg);
  box-shadow:
    0 36px 80px -28px rgba(0, 0, 0, 0.68),
    0 12px 24px -12px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(255, 255, 255, 0.06);
  max-width: min(88vw, 920px);
  max-height: 84vh;
  opacity: 0;
  transform-origin: 50% 30%;
}

.letter__paper img {
  display: block;
  width: 100%;
  height: auto;
  max-height: calc(84vh - 48px);
  object-fit: contain;
}

/* ==========================================================================
   FINAL VIDEO
   ========================================================================== */

.final {
  position: relative;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--sp-11) var(--gutter-desktop);
  background: var(--bg-final);
  isolation: isolate;
}

@media (max-width: 1023px) { .final { padding: var(--sp-9) var(--gutter-tablet); } }
@media (max-width: 767px)  { .final { padding: var(--sp-9) var(--gutter-mobile); } }

.final__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-7);
  position: relative;
  z-index: 1;
}

.final__polaroid {
  background: var(--polaroid-paper);
  padding: 14px 14px 44px;
  box-shadow: var(--polaroid-shadow);
  transform: rotate(-1.5deg);
  position: relative;
  width: clamp(280px, 32vw, 440px);
  margin: 0;
  cursor: pointer;
  transition: transform 600ms var(--ease-smooth);
  opacity: 0;
}

.final__polaroid:hover { transform: rotate(0deg) scale(1.02); }

.final__photo {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #1a1a1a;
  position: relative;
  overflow: hidden;
}

.final__photo-inner {
  position: absolute;
  inset: 0;
  background-image:
    var(--photo-url, none),
    linear-gradient(135deg,
      rgba(229, 184, 109, 0.25) 0%,
      rgba(26, 26, 26, 0.8) 60%,
      rgba(198, 107, 92, 0.25) 100%);
  background-size: cover, cover;
  background-position: center, center;
  display: grid;
  place-items: center;
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.final__photo-inner::before { content: ""; }

.final__photo-inner img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.final__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--polaroid-paper);
  transition: transform 320ms var(--ease-smooth);
}

.final__play:hover { transform: scale(1.08); }

.final__play svg {
  width: 64px;
  height: 64px;
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.6));
}

.final__caption {
  margin-top: var(--sp-3);
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: rgba(26, 26, 26, 0.55);
  text-transform: lowercase;
}

.final__hint {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--fs-h3);
  font-style: italic;
  color: var(--text-muted);
  letter-spacing: -0.01em;
  opacity: 0;
}

.final__player {
  position: fixed;
  inset: 0;
  z-index: var(--z-player);
  background: rgba(0, 0, 0, 0.96);
  display: grid;
  place-items: center;
  padding: var(--sp-7);
  opacity: 0;
  visibility: hidden;
  transition: opacity 480ms var(--ease-smooth), visibility 480ms;
}

.final__player.is-open {
  opacity: 1;
  visibility: visible;
}

.final__player video,
.final__player mux-player {
  width: 100%;
  max-width: 1280px;
  max-height: calc(100vh - var(--sp-9));
  background: #000;
  border-radius: 4px;
  box-shadow: 0 40px 80px -20px rgba(0, 0, 0, 0.8);
  /* mux-player tokens — match the rest of the gift's gold accent */
  --primary-color: var(--accent-gold);
  --secondary-color: rgba(0, 0, 0, 0.55);
  --media-control-bar-display: flex;
}

.final__close {
  position: absolute;
  top: var(--sp-5);
  right: var(--sp-5);
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-primary);
  font-size: 28px;
  line-height: 1;
  display: grid;
  place-items: center;
  transition: background 200ms ease;
}

.final__close:hover { background: rgba(255, 255, 255, 0.16); }

/* ==========================================================================
   ACCESSIBILITY + MOBILE FALLBACK
   ========================================================================== */

:focus-visible {
  outline: 2px solid var(--accent-gold);
  outline-offset: 4px;
  border-radius: 2px;
}

/* If CDN animation libraries fail to load, keep the site readable. */
.is-static .opening {
  min-height: auto;
  padding-top: var(--sp-9);
  padding-bottom: var(--sp-9);
}

.is-static .frame,
.is-static .opening__caption,
.is-static .opening__scroll,
.is-static .group__photo,
.is-static .final__polaroid,
.is-static .final__hint {
  opacity: 1 !important;
  transform: none !important;
}

.is-static .gallery {
  height: auto;
  overflow: visible;
}

.is-static .gallery__viewport {
  height: auto;
  overflow: visible;
}

.is-static .gallery__stream {
  flex-direction: column;
  height: auto;
  transform: none !important;
}

/* On mobile, horizontal pinning is awkward — fall back to vertical stream */
@media (max-width: 767px) {
  .gallery { height: auto; }
  .gallery__viewport { overflow: visible; height: auto; }
  .gallery__stream {
    flex-direction: column;
    align-items: center;
    height: auto;
    padding: var(--sp-7) var(--gutter-mobile);
    gap: var(--sp-9);
    transform: none !important;
    background: var(--bg-base);
  }
  .photo {
    --photo-h: auto !important;
    width: 100%;
    height: auto;
    transform: rotate(0) !important;
    opacity: 1 !important;
  }
  .photo__inner {
    height: auto;
    aspect-ratio: var(--photo-aspect, 3 / 4);
  }
  /* Handwritten notes: drop the absolute positioning + tilt on mobile so they
     stack neatly under their photo instead of overlapping it. */
  .handwritten {
    position: relative;
    inset: auto !important;
    max-width: 100%;
    margin: var(--sp-3) auto 0;
    transform: rotate(0deg) !important;
  }
  .frame3d { width: clamp(220px, 70vw, 320px); }
}

/* Static / no-JS fallback: handwritten notes flow inline under their photo */
.is-static .handwritten {
  position: relative;
  inset: auto !important;
  max-width: 100%;
  margin: var(--sp-3) 0 0;
  transform: rotate(0deg) !important;
  opacity: 1 !important;
}
