/* ========================================================================
   rituals.css — 六大仪式:票根 / 片名卡 / 显影 / 庆祝 / 烟花秀 / 信 / 登机牌
   ======================================================================== */

/* ================= ① 票根入场 ================= */
.gate {
  position: fixed;
  inset: 0;
  z-index: var(--z-gate);
  display: grid;
  place-items: center;
  gap: 0;
  /* 半透明:让星空在票根后隐约闪烁 */
  background:
    radial-gradient(90% 70% at 50% 42%, oklch(0.20 0.045 285 / 0.82) 0%, oklch(0.13 0.03 272 / 0.94) 100%);
  transition: opacity 1.1s var(--e-soft);
}
.gate.is-leaving { opacity: 0; pointer-events: none; }

.gate-inner {
  display: grid;
  gap: 2rem;
  justify-items: center;
  width: min(30rem, calc(100vw - 2.5rem));
}

.ticket {
  --stub-w: 7.2rem;
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--stub-w);
  width: 100%;
  border-radius: var(--radius-lg);
  background:
    linear-gradient(155deg, oklch(0.30 0.05 295) 0%, oklch(0.24 0.045 290) 55%, oklch(0.27 0.05 300) 100%);
  box-shadow:
    0 0 0 1px oklch(0.82 0.12 85 / 0.28),
    0 30px 80px -20px oklch(0.03 0.02 275 / 0.9),
    0 0 60px -10px oklch(0.82 0.12 85 / 0.12);
  cursor: pointer;
  transition: transform 0.6s var(--e-out), box-shadow 0.6s var(--e-out);
}
.ticket:hover {
  transform: translateY(-4px) rotate(-0.5deg);
  box-shadow:
    0 0 0 1px oklch(0.82 0.12 85 / 0.45),
    0 36px 90px -20px oklch(0.03 0.02 275 / 0.9),
    0 0 80px -8px oklch(0.82 0.12 85 / 0.2);
}
/* 齿孔切口(上下两个半圆) */
.ticket::before, .ticket::after {
  content: '';
  position: absolute;
  right: calc(var(--stub-w, 7.2rem) - 9px);
  width: 18px; height: 18px;
  border-radius: 50%;
  background: oklch(0.145 0.033 275);
  z-index: 1;
}
.ticket::before { top: -9px; }
.ticket::after  { bottom: -9px; }

.ticket-main {
  padding: clamp(1.4rem, 4.5vw, 2.2rem) clamp(1.4rem, 4.5vw, 2.4rem);
  display: grid;
  gap: 0.85rem;
  justify-items: start;
  min-width: 0;
}
.tk-admit {
  font-family: var(--font-display);
  font-size: var(--t-meta);
  letter-spacing: 0.42em;
  color: var(--gold);
}
.tk-title {
  font-size: clamp(1.8rem, 6.2vw, 2.5rem);
  font-weight: 400;
  letter-spacing: 0.24em;
  line-height: 1.3;
}
.tk-sub {
  font-size: var(--t-small);
  color: var(--ivory-dim);
  letter-spacing: 0.1em;
}
.tk-row {
  display: flex;
  gap: 1.6em;
  font-family: var(--font-display);
  font-size: var(--t-small);
  letter-spacing: 0.18em;
  color: var(--gold);
  border-top: 1px solid oklch(0.82 0.12 85 / 0.18);
  padding-top: 0.85rem;
  width: 100%;
  flex-wrap: wrap;
}
.tk-line {
  font-size: var(--t-meta);
  letter-spacing: 0.22em;
  color: var(--ivory-faint);
}

.ticket-stub {
  width: var(--stub-w);
  border-left: 2px dashed oklch(0.82 0.12 85 / 0.3);
  display: grid;
  place-content: center;
  gap: 0.7rem;
  justify-items: center;
  padding: 1rem 0.6rem;
  transition: transform 0.9s var(--e-out), opacity 0.9s var(--e-out);
  transform-origin: 10% 110%;
}
.tk-stub-txt {
  font-family: var(--font-display);
  letter-spacing: 0.32em;
  text-indent: 0.32em;
  color: var(--gold);
  writing-mode: vertical-rl;
  font-size: 1.05rem;
}
.tk-stub-cta {
  font-size: var(--t-meta);
  color: var(--ivory-dim);
  letter-spacing: 0.14em;
  writing-mode: vertical-rl;
}
/* 撕票 */
.ticket.is-torn { cursor: default; }
.ticket.is-torn .ticket-stub {
  transform: rotate(14deg) translate(46%, 24%);
  opacity: 0;
}
.ticket.is-torn .ticket-main { animation: tornNudge 0.5s var(--e-out); }
@keyframes tornNudge {
  30% { transform: translateX(-7px) rotate(-0.6deg); }
  100% { transform: none; }
}

.gate-hint {
  font-size: var(--t-meta);
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  color: var(--ivory-faint);
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
}
.gate-hint::before, .gate-hint::after {
  content: '';
  width: 2rem; height: 1px;
  background: oklch(1 0 0 / 0.14);
}

/* ---- 胶片倒数 ---- */
.countdown {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}
.countdown.is-on { opacity: 1; }
.cd-circle {
  position: relative;
  width: min(15rem, 60vw);
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid oklch(1 0 0 / 0.22);
  display: grid;
  place-items: center;
  /* 十字线 */
  background:
    linear-gradient(0deg, transparent calc(50% - 0.5px), oklch(1 0 0 / 0.14) 50%, transparent calc(50% + 0.5px)),
    linear-gradient(90deg, transparent calc(50% - 0.5px), oklch(1 0 0 / 0.14) 50%, transparent calc(50% + 0.5px));
}
/* 雷达扫掠 */
.cd-circle::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg, oklch(1 0 0 / 0.10) var(--sweep), transparent var(--sweep));
}
.cd-circle.is-sweeping::after { animation: cdSweep 0.72s linear infinite; }
@keyframes cdSweep { from { --sweep: 0deg; } to { --sweep: 360deg; } }
.cd-num {
  font-family: var(--font-display);
  font-size: clamp(5rem, 20vw, 8rem);
  color: var(--ivory);
  line-height: 1;
}

/* ================= 片名卡(序幕封面) ================= */
.cover {
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: 0 var(--pad-x);
  position: relative;
}
.cover-inner { display: grid; gap: 1.6rem; justify-items: center; text-align: center; }
.cover-kicker {
  font-family: var(--font-display);
  font-size: var(--t-small);
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  color: var(--gold);
}
.cover-title {
  font-size: var(--t-title);
  font-weight: 400;
  letter-spacing: 0.22em;
  text-indent: 0.11em;
  line-height: 1.24;
  text-shadow: 0 0 70px oklch(0.82 0.12 85 / 0.22);
}
.cover-title .char { display: inline-block; } /* SplitText 兜底 */
.cover-line {
  font-size: var(--t-body);
  color: var(--ivory-dim);
  letter-spacing: 0.08em;
  max-width: 30em;
}
.scroll-cue {
  position: absolute;
  bottom: max(2rem, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 0.6rem;
}
.scroll-cue-text {
  font-size: var(--t-meta);
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  color: var(--ivory-faint);
}
.scroll-cue-track {
  width: 1px; height: 3.4rem;
  background: oklch(1 0 0 / 0.12);
  position: relative;
  overflow: hidden;
}
.scroll-cue-track::after {
  content: '';
  position: absolute;
  left: 0; top: -40%;
  width: 100%; height: 40%;
  background: linear-gradient(180deg, transparent, var(--gold));
  animation: cueDrop 2.1s var(--e-soft) infinite;
}
@keyframes cueDrop {
  0% { top: -40%; }
  70%, 100% { top: 110%; }
}

/* ================= ② 长按显影 ================= */
[data-develop] .photo-grade { --dev: 0; }
[data-develop] .photo-grade img {
  filter:
    brightness(calc(0.16 + 0.84 * var(--dev)))
    saturate(calc(0.2 + 0.74 * var(--dev)))
    contrast(calc(0.92 + 0.14 * var(--dev)))
    blur(calc((1 - var(--dev)) * 10px));
  transition: none;
}
[data-develop].is-developed .photo-grade img {
  filter: contrast(1.06) saturate(0.94) brightness(0.98);
  transition: filter 0.7s var(--e-soft);
}
/* 完成柔闪 */
[data-develop] .photo-grade .dev-flash {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: oklch(0.97 0.02 85);
  opacity: 0;
  pointer-events: none;
}
[data-develop].is-developed .photo-grade .dev-flash {
  animation: devFlash 0.9s var(--e-soft);
}
@keyframes devFlash {
  0% { opacity: 0; }
  18% { opacity: 0.75; }
  100% { opacity: 0; }
}
/* 显影提示章(盖在照片上) */
.dev-chip {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 1.1rem;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.55em 1.05em;
  font-size: var(--t-meta);
  letter-spacing: 0.2em;
  color: var(--ivory);
  background: oklch(0.14 0.03 275 / 0.62);
  border: 1px solid oklch(1 0 0 / 0.2);
  border-radius: 999px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: opacity 0.6s var(--e-out);
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  cursor: pointer;
}
.dev-chip svg { display: block; }
.dev-chip .dev-ring-bg { stroke: oklch(1 0 0 / 0.2); }
.dev-chip .dev-ring-fg {
  stroke: var(--gold);
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: center;
}
[data-develop].is-developed .dev-chip { opacity: 0; pointer-events: none; }
/* 显影中的暗房红光氛围 */
[data-develop] .photo-grade::after {
  background:
    linear-gradient(160deg,
      oklch(0.55 0.16 25 / calc((1 - var(--dev)) * 0.30)) 0%,
      transparent 50%,
      oklch(0.45 0.14 20 / calc((1 - var(--dev)) * 0.22)) 100%),
    linear-gradient(160deg,
      oklch(0.75 0.11 80 / calc(var(--photo-warmth) * 0.9)) 0%,
      transparent 42%,
      oklch(0.45 0.12 20 / calc(var(--photo-warmth) * 0.7)) 100%);
}

/* ================= ③ NTU 庆祝 ================= */
.flash {
  position: fixed;
  inset: 0;
  z-index: var(--z-burn);
  background: oklch(0.97 0.02 85);
  opacity: 0;
  pointer-events: none;
}
.flash.is-firing { animation: camFlash 0.62s var(--e-soft); }
@keyframes camFlash {
  0% { opacity: 0; }
  12% { opacity: 0.85; }
  100% { opacity: 0; }
}
.stamp {
  position: absolute;
  z-index: 3;
  top: 7%;
  right: 6%;
  width: clamp(5.6rem, 22vw, 7.6rem);
  aspect-ratio: 1;
  border: 2px solid oklch(0.82 0.12 85 / 0.85);
  border-radius: 50%;
  display: grid;
  place-content: center;
  gap: 0.15rem;
  justify-items: center;
  transform: rotate(-14deg) scale(1.8);
  opacity: 0;
  pointer-events: none;
  background: oklch(0.2 0.04 285 / 0.25);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.stamp .stamp-en {
  font-family: var(--font-display);
  font-size: 0.9em;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  color: var(--gold);
}
.stamp .stamp-date {
  font-family: var(--font-display);
  font-size: 0.62em;
  letter-spacing: 0.3em;
  color: var(--ivory-dim);
}
.stamp.is-in {
  animation: stampIn 0.7s var(--e-back) forwards;
}
@keyframes stampIn {
  0% { opacity: 0; transform: rotate(-2deg) scale(1.9); }
  55% { opacity: 1; transform: rotate(-15deg) scale(0.94); }
  100% { opacity: 1; transform: rotate(-13deg) scale(1); }
}

/* ================= ④ 迪士尼烟花秀 ================= */
.t-fwscene {
  position: relative;
  min-height: 100svh;
  padding: 0;
  display: grid;
  overflow: clip;
}
.t-fwscene .fw-bg {
  position: absolute;
  inset: 0;
  border-radius: 0;
  box-shadow: none;
}
.t-fwscene .fw-bg img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 68%; /* 城堡居中偏下,给上方夜空留舞台 */
  filter: contrast(1.05) saturate(1.02) brightness(0.9);
}
.t-fwscene .fw-veil {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, oklch(0.1 0.03 275 / 0.68) 0%, transparent 34%, transparent 70%, oklch(0.1 0.03 275 / 0.82) 100%);
  pointer-events: none;
}
.t-fwscene .fw-ui {
  position: relative;
  z-index: 2;
  display: grid;
  align-content: space-between;
  justify-items: center;
  padding: clamp(4.5rem, 12vh, 7rem) var(--pad-x) clamp(2.4rem, 8vh, 4.5rem);
  min-height: 100svh;
  pointer-events: none; /* 让点击落到 section(发射烟花) */
}
.fw-hint {
  display: inline-flex;
  align-items: center;
  gap: 0.8em;
  font-size: var(--t-small);
  letter-spacing: 0.22em;
  color: var(--ivory);
  text-shadow: 0 1px 12px oklch(0.05 0.02 275 / 0.9);
  opacity: 0;
  transition: opacity 1s var(--e-soft);
}
.fw-hint.is-on { opacity: 1; }
.fw-hint-ring {
  width: 0.85em; aspect-ratio: 1;
  border: 1px solid var(--gold);
  border-radius: 50%;
  position: relative;
}
.fw-hint-ring::after {
  content: '';
  position: absolute;
  inset: -6px;
  border: 1px solid oklch(0.82 0.12 85 / 0.5);
  border-radius: 50%;
  animation: hintPulse 2s var(--e-soft) infinite;
}
@keyframes hintPulse {
  0% { transform: scale(0.6); opacity: 1; }
  100% { transform: scale(1.5); opacity: 0; }
}
.fw-finale-btn { pointer-events: auto; justify-self: center; }
.fw-finale-btn[hidden] { display: none; }
.fw-caption {
  display: grid;
  gap: 0.4rem;
  justify-items: center;
  text-align: center;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 1.2s var(--e-soft), transform 1.2s var(--e-out);
}
.fw-caption.is-on { opacity: 1; transform: none; }
.fw-caption .sub-text {
  font-family: var(--font-hand);
  font-size: calc(var(--t-cap) * 1.08);
  color: var(--ivory);
  text-shadow: 0 1px 14px oklch(0.05 0.02 275 / 0.95);
}

/* ================= ⑤ 最后一封信 ================= */
.letter {
  position: relative;
  padding: calc(var(--gap-ch) / 1.5) var(--pad-x);
  display: grid;
  justify-items: center;
}
.letter-stage {
  width: min(40rem, 100%);
  display: grid;
  justify-items: center;
  perspective: 1200px;
}
/* 信封与信纸同格重叠:拆开后信纸原地接棒 */
.letter-stage > * { grid-area: 1 / 1; }
.letter-stage .envelope-wrap { align-self: center; }

/* ---- 信封 ---- */
.envelope {
  position: relative;
  width: min(24rem, 88vw);
  aspect-ratio: 8 / 5;
  cursor: pointer;
  transform-style: preserve-3d;
  transition: transform 0.7s var(--e-out), opacity 0.8s var(--e-soft);
}
.envelope:hover { transform: translateY(-4px); }
.env-body, .env-flap {
  position: absolute;
  background:
    linear-gradient(165deg, oklch(0.93 0.025 80) 0%, oklch(0.88 0.03 78) 100%);
}
.env-body {
  inset: 0;
  border-radius: 8px;
  box-shadow:
    0 0 0 1px oklch(0.6 0.06 80 / 0.4),
    0 26px 70px -18px oklch(0.03 0.02 275 / 0.95),
    0 0 70px -14px oklch(0.82 0.12 85 / 0.22);
  overflow: hidden;
}
/* 信封内衬 V 纹 */
.env-body::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(115deg, transparent 48%, oklch(0.6 0.06 80 / 0.24) 49%, transparent 52%),
    linear-gradient(245deg, transparent 48%, oklch(0.6 0.06 80 / 0.24) 49%, transparent 52%);
}
.env-flap {
  inset: 0 0 auto 0;
  height: 54%;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  transform-origin: top center;
  transition: transform 1s var(--e-out);
  background: linear-gradient(180deg, oklch(0.90 0.028 79), oklch(0.84 0.032 76));
  box-shadow: inset 0 -1px 0 oklch(0.6 0.06 80 / 0.35);
  z-index: 2;
}
.envelope.is-open .env-flap { transform: rotateX(-176deg); z-index: 0; }
.env-to {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: 13%;
  transform: translateX(-50%);
  font-family: var(--font-hand);
  font-size: calc(var(--t-small) * 1.2);
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  color: oklch(0.4 0.04 60);
  white-space: nowrap;
}
/* 火漆封印 */
.wax {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 47%;
  transform: translate(-50%, -50%);
  width: 3.6rem;
  aspect-ratio: 1;
  border-radius: 47% 53% 50% 50% / 52% 48% 52% 48%;
  background:
    radial-gradient(60% 60% at 38% 32%, oklch(0.68 0.16 22), oklch(0.5 0.17 18) 70%, oklch(0.42 0.15 15));
  box-shadow:
    0 3px 10px oklch(0.05 0.02 275 / 0.5),
    inset 0 1px 3px oklch(0.9 0.05 30 / 0.55),
    inset 0 -2px 5px oklch(0.3 0.12 15 / 0.8);
  display: grid;
  place-items: center;
  color: oklch(0.92 0.03 30);
  font-size: 1.25rem;
  transition: transform 0.5s var(--e-back);
}
.envelope:hover .wax { transform: translate(-50%, -50%) scale(1.07); }
.envelope.is-open .wax {
  animation: waxPop 0.7s var(--e-out) forwards;
}
@keyframes waxPop {
  30% { transform: translate(-50%, -50%) scale(1.2) rotate(6deg); opacity: 1; }
  100% { transform: translate(-50%, -90%) scale(0.6) rotate(18deg); opacity: 0; }
}
.env-hint {
  margin-top: 1.6rem;
  font-size: var(--t-meta);
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  color: var(--ivory-faint);
  text-align: center;
}
.envelope-wrap { display: grid; justify-items: center; }
.envelope-wrap.is-away {
  opacity: 0;
  transform: translateY(30px) scale(0.94);
  pointer-events: none;
  transition: opacity 0.8s var(--e-soft), transform 0.8s var(--e-out);
}

/* ---- 信纸 ---- */
.paper {
  width: 100%;
  border-radius: 10px;
  background:
    linear-gradient(170deg, oklch(0.955 0.018 82) 0%, oklch(0.915 0.022 78) 100%);
  color: oklch(0.32 0.028 45);
  box-shadow:
    0 0 0 1px oklch(0.6 0.06 80 / 0.35),
    0 34px 90px -22px oklch(0.03 0.02 275 / 0.95),
    0 0 90px -16px oklch(0.82 0.12 85 / 0.25);
  padding: clamp(1.8rem, 6vw, 3.4rem) clamp(1.5rem, 5.5vw, 3rem) clamp(2.2rem, 7vw, 3.8rem);
  display: none;
  opacity: 0;
  transform: translateY(40px) scale(0.97);
}
.paper.is-in {
  display: block;
  animation: paperIn 1.1s var(--e-out) forwards;
}
@keyframes paperIn {
  from { opacity: 0; transform: translateY(46px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}
.paper-greeting {
  font-family: var(--font-hand);
  font-size: calc(var(--t-h3) * 1.12);
  font-weight: 400;
  letter-spacing: 0.06em;
  margin-bottom: 1.6rem;
}
.paper p {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 1s var(--e-soft), transform 1s var(--e-out);
}
.paper p.is-written { opacity: 1; transform: none; }
.paper .lt-lead {
  font-family: var(--font-hand);
  font-size: calc(var(--t-cap) * 1.15);
  margin-bottom: 1.2rem;
}
.paper .lt-hero {
  font-family: var(--font-brush);
  font-size: clamp(2.8rem, 11vw, 4.8rem);
  line-height: 1.3;
  color: oklch(0.52 0.16 20);
  text-align: center;
  margin: 1rem 0 1.8rem;
  text-shadow: 0 0 40px oklch(0.7 0.14 60 / 0.3);
}
.paper .lt-p {
  font-family: var(--font-hand);
  font-size: calc(var(--t-body) * 1.14);
  line-height: 2.1;
  margin-bottom: 1.35rem;
}
.paper .lt-p .hl {
  color: oklch(0.48 0.14 30);
  border-bottom: 1px solid oklch(0.7 0.13 70 / 0.7);
  padding-bottom: 1px;
}
.paper .lt-p em { font-style: normal; color: oklch(0.45 0.15 20); }
.paper .lt-vow {
  font-family: var(--font-hand);
  font-size: calc(var(--t-cap) * 1.15);
  font-weight: 400;
  text-align: center;
  margin: 2rem 0 0.6rem;
  color: oklch(0.42 0.1 35);
  animation: vowBreath 4.5s ease-in-out infinite;
}
@keyframes vowBreath {
  0%, 100% { text-shadow: 0 0 14px oklch(0.75 0.12 70 / 0.15); }
  50% { text-shadow: 0 0 30px oklch(0.75 0.12 70 / 0.5); }
}
.paper .lt-sign {
  font-family: var(--font-hand);
  font-size: clamp(1.5rem, 4.8vw, 2rem);
  text-align: right;
  color: oklch(0.4 0.06 40);
  margin-top: 2.2rem;
}

/* ---- NUS 校章:半透明油墨压印(multiply:墨压在纸纹上,字透墨可读) ---- */
.paper { position: relative; overflow: hidden; }
.nus-stamp {
  position: absolute;
  right: clamp(0.6rem, 4vw, 2.4rem);
  bottom: clamp(0.5rem, 3.5vw, 1.8rem);
  width: clamp(8.5rem, 30vw, 12rem);
  aspect-ratio: 1;
  pointer-events: none;
  color: oklch(0.46 0.16 27);            /* 印泥朱红 */
  mix-blend-mode: multiply;              /* 油墨逻辑:只加深,不遮盖 → 字透出 */
  opacity: 0;
  transform: rotate(4deg) scale(1.5);
  transform-origin: 62% 62%;
}
.nus-stamp svg { width: 100%; height: 100%; display: block; }
.nus-stamp .seal-ring {
  font-family: var(--font-display);
  font-size: 13.5px;
  letter-spacing: 2.6px;
  fill: currentColor;
}
.nus-stamp .seal-year {
  font-family: var(--font-display);
  font-size: 12.5px;
  letter-spacing: 3px;
  fill: currentColor;
}
.nus-stamp .seal-star { font-size: 9px; fill: currentColor; }
/* 盖章:一次干脆的落印 + 微回弹 */
.paper.is-stamped .nus-stamp {
  animation: nusStamp 0.62s cubic-bezier(0.22, 1.6, 0.36, 1) forwards;
}
@keyframes nusStamp {
  0%   { opacity: 0; transform: rotate(4deg) scale(1.5); }
  55%  { opacity: 0.92; transform: rotate(-6.5deg) scale(0.965); }
  78%  { opacity: 0.88; transform: rotate(-5.5deg) scale(1.015); }
  100% { opacity: 0.88; transform: rotate(-6deg) scale(1); }
}
/* 落印瞬间:纸面被压出一圈极淡的墨晕 */
.paper::after {
  content: '';
  position: absolute;
  right: clamp(0.6rem, 4vw, 2.4rem);
  bottom: clamp(0.5rem, 3.5vw, 1.8rem);
  width: clamp(8.5rem, 30vw, 12rem);
  aspect-ratio: 1;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(closest-side, oklch(0.46 0.16 27 / 0.12), transparent 72%);
  mix-blend-mode: multiply;
  opacity: 0;
  transform: scale(0.9);
}
.paper.is-stamped::after {
  animation: nusHalo 0.9s var(--e-out) 0.28s forwards;
}
@keyframes nusHalo {
  0% { opacity: 0; transform: scale(0.9); }
  40% { opacity: 1; }
  100% { opacity: 0.55; transform: scale(1.06); }
}

/* ================= ⑥ 尾声登机牌 ================= */
.coda {
  min-height: 88svh;
  display: grid;
  place-items: center;
  padding: var(--gap-ch) var(--pad-x) calc(var(--gap-ch) / 1.4);
}
.coda-inner { display: grid; gap: 2.6rem; justify-items: center; width: 100%; }
.boarding {
  width: min(30rem, 100%);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(155deg, oklch(0.30 0.05 295) 0%, oklch(0.24 0.045 290) 55%, oklch(0.27 0.05 300) 100%);
  box-shadow:
    0 0 0 1px oklch(0.82 0.12 85 / 0.28),
    0 30px 80px -20px oklch(0.03 0.02 275 / 0.9);
  padding: clamp(1.5rem, 5vw, 2.2rem) clamp(1.5rem, 5vw, 2.4rem);
  display: grid;
  gap: 1.3rem;
}
.bp-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-family: var(--font-display);
  letter-spacing: 0.26em;
  font-size: var(--t-meta);
  color: var(--gold);
  border-bottom: 1px solid oklch(0.82 0.12 85 / 0.18);
  padding-bottom: 0.9rem;
  flex-wrap: wrap;
}
.bp-route {
  display: grid;
  gap: 0.2rem;
}
/* ---- 航线图:深圳 → 新加坡 ---- */
.bp-map {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}
.bp-ghost {
  fill: none;
  stroke: oklch(0.82 0.12 85 / 0.30);
  stroke-width: 1;
  stroke-dasharray: 0.9 2.6;   /* pathLength=100 归一化 */
  stroke-linecap: round;
}
.bp-trail {
  fill: none;
  stroke: url(#bp-trail-grad);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;      /* SMIL 动画推进到 0 */
  filter: drop-shadow(0 0 4px oklch(0.82 0.12 85 / 0.55));
}
.bp-dot {
  fill: oklch(0.82 0.12 85);
}
.bp-dot--dest { fill: oklch(0.62 0.14 25); }
.bp-pulse {
  fill: none;
  stroke: oklch(0.62 0.14 25 / 0.8);
  stroke-width: 1;
  opacity: 0;
  transform-origin: 298px 80px;
}
.bp-heart {
  font-size: 13px;
  fill: oklch(0.74 0.11 20);
  opacity: 0;
}
.bp-plane {
  fill: oklch(0.9 0.06 85);
  filter: drop-shadow(0 0 6px oklch(0.82 0.12 85 / 0.8));
  opacity: 0;                   /* 起飞时亮起 */
  offset-rotate: auto;
}
.boarding.is-flying .bp-plane { opacity: 1; }
/* 降落:飞机淡出,目的地心跳亮起 */
.boarding.is-flying .bp-plane { animation: bpPlaneLife 3.9s var(--e-soft) forwards; }
@keyframes bpPlaneLife {
  0% { opacity: 0; }
  6% { opacity: 1; }
  92% { opacity: 1; }
  100% { opacity: 0; }
}
.boarding.is-flying .bp-pulse { animation: bpPulse 1.2s var(--e-out) 3.5s 2; }
.boarding.is-flying .bp-heart { animation: bpHeartIn 0.8s var(--e-back) 3.6s forwards; }
@keyframes bpPulse {
  0% { opacity: 0.9; transform: scale(0.4); }
  100% { opacity: 0; transform: scale(1.8); }
}
@keyframes bpHeartIn {
  0% { opacity: 0; transform: translateY(6px) scale(0.6); transform-origin: 298px 66px; }
  100% { opacity: 1; transform: none; }
}
/* 终态(reduced-motion / SMIL 失败 / QA):航迹全亮,心已抵达 */
.boarding.is-landed .bp-trail { stroke-dashoffset: 0; }
.boarding.is-landed .bp-heart { opacity: 1; }
.boarding.is-landed .bp-plane { opacity: 0; }

.bp-cities {
  display: flex;
  justify-content: space-between;
  margin-top: -0.4rem;
}
.bp-city {
  display: grid;
  gap: 0.2rem;
  justify-items: center;
}
.bp-code {
  font-family: var(--font-display);
  font-size: clamp(2rem, 8vw, 2.9rem);
  letter-spacing: 0.08em;
  line-height: 1;
}
.bp-name { font-size: var(--t-meta); letter-spacing: 0.3em; text-indent: 0.3em; color: var(--ivory-dim); }
.bp-line {
  text-align: center;
  font-size: var(--t-body);
  color: var(--ivory);
  letter-spacing: 0.1em;
}
.coda-sign {
  font-family: var(--font-hand);
  font-size: clamp(1.6rem, 5.2vw, 2.1rem);
  color: var(--ivory);
  text-align: center;
}
.coda-heart {
  color: var(--rose-soft);
  font-size: 1.4rem;
  animation: heartBeat 2.6s ease-in-out infinite;
}
@keyframes heartBeat {
  0%, 100% { transform: scale(1); opacity: 0.85; }
  12% { transform: scale(1.18); opacity: 1; }
  24% { transform: scale(1); }
}

/* ================= reduced-motion ================= */
@media (prefers-reduced-motion: reduce) {
  .cd-circle.is-sweeping::after { animation: none; }
  .scroll-cue-track::after { animation: none; }
  .fw-hint-ring::after { animation: none; }
  .paper .lt-vow { animation: none; }
  .coda-heart { animation: none; }
  .paper.is-stamped .nus-stamp { animation: none; opacity: 0.88; transform: rotate(-6deg); }
  .paper.is-stamped::after { animation: none; opacity: 0.55; transform: scale(1.06); }
}

/* ================= 移动端微调 ================= */
@media (max-width: 720px) {
  .ticket { --stub-w: 5.6rem; }
  .ticket-main { padding-right: 1.1rem; }
  .tk-row { gap: 0.9em; }
  .stamp { top: 4%; right: 4%; }
  .t-fwscene .fw-bg img { object-position: 50% 74%; }
}
