﻿:root {
  color-scheme: light;
  --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-strong: cubic-bezier(0.16, 1, 0.3, 1);
  --font-zh: '上图东观体', 'Dancing Script', cursive;
  --font-en: 'Dancing Script', '上图东观体', cursive;
  --surface-page: oklch(95.8% 0.018 82);
  --surface-page-soft: oklch(97.2% 0.016 78);
  --surface-paper: oklch(96.2% 0.026 76);
  --surface-paper-strong: oklch(92.4% 0.024 76);
  --ink-warm: oklch(30% 0.035 52);
  --ink-muted: oklch(46% 0.028 54);
  --accent-rose: oklch(68% 0.16 17);
  --accent-rose-strong: oklch(59% 0.18 17);
  --accent-gold: oklch(83% 0.13 84);
  --accent-gold-soft: oklch(92% 0.08 84);
  --accent-sage: oklch(76% 0.10 145);
  --focus-ring: oklch(80% 0.16 83);
  --shadow-warm: oklch(35% 0.05 53 / 0.16);
  --shadow-deep: oklch(28% 0.04 53 / 0.26);
  --spring-pink: #e9b7b6;
  --spring-yellow: #ead27d;
  --summer-blue: #83c5d7;
  --summer-green: #9fc88b;
  --autumn-red: #ca7254;
  --autumn-orange: #e6a25d;
  --winter-white: #f0f3fa;
  --winter-blue: #8ea7c4;
}

/* ===== Reset ===== */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ===== Base ===== */
html, body {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  font-family: var(--font-zh);
  background: var(--surface-page);
}

/* ===== Layout Skeleton ===== */

/* Main container — all state DOM injected here by JS */
#app {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 1;
}

/* Disable all interaction during animated transitions */
#app.locked {
  pointer-events: none;
}

/* Dedicated dog PNG layer — floats above scene content */
#character-layer {
  position: absolute;
  inset: 0;
  z-index: 50;
  pointer-events: none;
}

/* Full-viewport canvas — CAKE scratch-erase & FIREWORK_TEXT particles */
#fx-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 10;
  pointer-events: none;
  display: none;
}

/* ===== Cursor Glow ===== */
/* Activated / deactivated per state via JS (display: block / none) */
#cursor-glow {
  position: fixed;
  width: 200px;
  height: 200px;
  background: radial-gradient(circle, oklch(95% 0.09 83 / 0.30) 0%, oklch(93% 0.04 19 / 0.14) 34%, transparent 68%);
  border-radius: 50%;
  pointer-events: none;
  z-index: 100;
  transform: translate(-50%, -50%);
  display: none;
}

button:focus-visible,
[role="button"]:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 4px;
}

.gallery-card:focus-visible {
  box-shadow:
    0 0 0 3px var(--surface-page-soft),
    0 0 0 7px oklch(80% 0.12 83 / 0.72),
    0 16px 30px var(--shadow-warm);
}

/* ===== Gallery Photo ===== */
/* Applied to <img> inside each gallery card */
.gallery-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  mask-image: radial-gradient(ellipse, black 60%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse, black 60%, transparent 100%);
  transition: filter 0.3s ease, transform 0.3s ease;
}

.gallery-photo:hover {
  filter:
    drop-shadow(0 0 14px oklch(91% 0.12 84 / 0.72))
    drop-shadow(0 0 6px oklch(68% 0.16 17 / 0.18));
  transform: scale(1.05);
}

/* ===== Letter Paper ===== */
/* Used in LETTER state — textured ivory paper with ruled lines & red margin */
.letter-paper {
  background-color: var(--surface-paper);
  background-image:
    /* Red margin line — classic notebook/letter style */
    linear-gradient(to right, transparent 62px, oklch(63% 0.16 18 / 0.45) 62px, oklch(63% 0.16 18 / 0.45) 64px, transparent 64px),
    /* Subtle edge darkening — aged paper vignette */
    radial-gradient(ellipse at center, transparent 45%, oklch(76% 0.04 72 / 0.18) 100%),
    /* Horizontal ruled lines */
    repeating-linear-gradient(
      transparent, transparent 41px,
      oklch(72% 0.04 74 / 0.35) 41px, oklch(72% 0.04 74 / 0.35) 42px
    );
  line-height: 42px;
  font-family: var(--font-en);
  overflow-y: auto;
  position: relative;
}

/* Noise texture + aged stain spots */
.letter-paper::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    /* Coffee / tea stain spot — top right area */
    radial-gradient(ellipse 90px 80px at 78% 12%, oklch(73% 0.05 72 / 0.10), transparent 70%),
    /* Faint age spot — bottom left */
    radial-gradient(ellipse 70px 60px at 18% 85%, oklch(70% 0.04 68 / 0.07), transparent 65%),
    /* Paper fiber noise texture */
    url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.7'/%3E%3C/svg%3E");
  opacity: 0.22;
  mix-blend-mode: multiply;
  z-index: 0;
}

/* ===== Mobile / Portrait Blocker ===== */
#mobile-block {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9999;
  background:
    radial-gradient(ellipse at center, oklch(98% 0.012 82 / 0.42) 0%, transparent 60%),
    radial-gradient(ellipse, transparent 65%, oklch(74% 0.04 70 / 0.14) 100%),
    linear-gradient(135deg, oklch(98% 0.014 82) 0%, oklch(95% 0.02 76) 50%, oklch(90% 0.03 68) 100%);
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 1.6rem;
  text-align: center;
  padding: 2.5rem;
}

#mobile-block img {
  width: 100px;
  height: auto;
  opacity: 0.85;
  pointer-events: none;
  user-select: none;
}

#mobile-block p {
  font-family: var(--font-zh);
  font-size: 1.55rem;
  color: var(--accent-rose-strong);
  letter-spacing: 0.08em;
  line-height: 1.8;
}

#mobile-block small {
  display: block;
  font-family: var(--font-en);
  font-size: 1.1rem;
  color: oklch(58% 0.06 58);
  margin-top: 0.3rem;
  letter-spacing: 0.04em;
}

@media (max-width: 768px), (orientation: portrait) {
  #mobile-block {
    display: flex;
  }
  #app, #fx-canvas, #character-layer {
    display: none !important;
  }
}

/* ===== Keyframe Animations ===== */

/* General purpose fade */
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes fadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* SPRING: peach grove slides out to the left */
@keyframes slideOutLeft {
  from { transform: translateX(0); }
  to   { transform: translateX(-100vw); }
}

/* SPRING: mango grove slides in from the right */
@keyframes slideInRight {
  from { transform: translateX(100vw); }
  to   { transform: translateX(0); }
}

/* SPRING: dog gently bobs while running in peach grove */
@keyframes floatBob {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-8px); }
}

@keyframes floatBobPulse {
  0%, 100% { transform: translateY(0);    opacity: 1; }
  50%       { transform: translateY(-8px); opacity: 0.55; }
}

/* SPRING: dog jumps in mango grove */
@keyframes jumpUp {
  0%, 100% { transform: translateY(0); }
  40%       { transform: translateY(-28px); }
}

/* SPRING: smaller jump for peach grove click */
@keyframes jumpUpSmall {
  0%, 100% { transform: translateY(0); }
  40%       { transform: translateY(-16px); }
}

/* SUMMER: boat rocks driven by wave-near (3.2s, 1.4s delay)
   Wave surges toward shore (−x, +y) → boat tilts left/into-shore at peak */
@keyframes sway {
  0%   { transform: rotate(0deg)   translateY(0);    }
  18%  { transform: rotate(-2deg)  translateY(-2px); }
  30%  { transform: rotate(-4deg)  translateY(-6px); }
  42%  { transform: rotate(-6deg)  translateY(-9px); }
  52%  { transform: rotate(-5deg)  translateY(-8px); }
  64%  { transform: rotate(-3deg)  translateY(-5px); }
  76%  { transform: rotate(0deg)   translateY(-2px); }
  88%  { transform: rotate(2deg)   translateY(-1px); }
  100% { transform: rotate(0deg)   translateY(0);    }
}

/* SUMMER: coconut falls from top with rotation — stays on ground */
@keyframes fallDown {
  from { transform: translateY(-100px) rotate(0deg);   opacity: 1; }
  to   { transform: translateY(calc(100vh - 140px)) rotate(360deg); opacity: 1; }
}

/* WINTER: snowFall removed — snow now drawn via Canvas rAF */

/* SPRING: clip-path circle reveal from dog position — peach → mango */
@keyframes clipReveal {
  from {
    clip-path: circle(0% at var(--reveal-x, 50%) var(--reveal-y, 50%));
    -webkit-clip-path: circle(0% at var(--reveal-x, 50%) var(--reveal-y, 50%));
  }
  to {
    clip-path: circle(150% at var(--reveal-x, 50%) var(--reveal-y, 50%));
    -webkit-clip-path: circle(150% at var(--reveal-x, 50%) var(--reveal-y, 50%));
  }
}

/* WINTER phase 2: full-screen white flash before indoor reveal */
@keyframes whiteFlash {
  0%   { opacity: 0; }
  50%  { opacity: 1; }
  100% { opacity: 0; }
}

.season-character {
  pointer-events: none;
  user-select: none;
}

#spring-viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.spring-panel {
  position: absolute;
  inset: 0;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}

.spring-panel-peach {
  mask-image: linear-gradient(to right, black 75%, transparent 100%);
  -webkit-mask-image: linear-gradient(to right, black 75%, transparent 100%);
}

/* Mango panel — clip-path circle reveal from dog position */
.spring-panel-mango {
  z-index: 2;
  clip-path: circle(0% at var(--reveal-x, 50%) var(--reveal-y, 50%));
  -webkit-clip-path: circle(0% at var(--reveal-x, 50%) var(--reveal-y, 50%));
  animation: clipReveal var(--mango-reveal-duration, 1s) ease-in-out forwards;
}

#spring-hint {
  position: absolute;
  right: 54px;
  bottom: 48px;
  padding: 0.75rem 1.9rem;
  border: 1px solid oklch(68% 0.10 17 / 0.28);
  border-radius: 999px;
  background: linear-gradient(180deg, oklch(98% 0.013 80 / 0.95), oklch(94% 0.024 76 / 0.92));
  color: var(--accent-rose-strong);
  font-family: var(--font-zh);
  font-size: 1.55rem;
  letter-spacing: 0.1em;
  box-shadow: 0 16px 32px oklch(54% 0.06 52 / 0.16);
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.35s var(--ease-out-soft), transform 0.35s var(--ease-out-soft), background 0.25s ease, box-shadow 0.25s ease;
  cursor: pointer;
}

#spring-hint.visible {
  opacity: 1;
  transform: translateY(0);
  animation: hintPulse 2.4s ease-in-out 2s infinite;
}

#spring-hint:hover {
  background: linear-gradient(180deg, oklch(99% 0.012 82), oklch(95% 0.03 84));
  box-shadow: 0 18px 36px oklch(54% 0.06 52 / 0.20);
}

.spring-dog {
  position: fixed;
  left: 2vw;
  bottom: 72px;
  width: min(18vw, 240px);
  height: auto;
  z-index: 60;
  /* No animation by default — dog stays still until clicked */
  transition: left 0.8s ease-in-out;
  filter: drop-shadow(0 14px 18px oklch(34% 0.05 53 / 0.18));
}

/* Single jump animation triggered on click */
.spring-dog.jumping {
  animation: jumpUpSmall 0.5s ease-in-out 1;
}

.mango-jump-wrap {
  position: fixed;
  left: 75%;
  top: 70%;
  z-index: 60;
  transform: translate(-50%, -50%);
}

.mango-jump-dog {
  /* 放大约 20%：从 min(18vw, 240px) → min(22vw, 288px) */
  width: min(22vw, 288px);
  height: auto;
  animation: jumpUp var(--mango-jump-duration, 0.6s) ease-in-out 2;
  animation-fill-mode: both;
  filter: drop-shadow(0 14px 18px rgba(126, 88, 61, 0.18));
  transform-origin: center;
}

.summer-reveal {
  position: absolute;
  inset: 0;
  clip-path: circle(0% at var(--reveal-x, 50%) var(--reveal-y, 88%));
  -webkit-clip-path: circle(0% at var(--reveal-x, 50%) var(--reveal-y, 88%));
  will-change: clip-path;
  animation: clipReveal var(--summer-reveal-duration, 1.2s) cubic-bezier(0.22, 1, 0.36, 1) forwards;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}

#summer-ocean-haze {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, transparent 24%),
    linear-gradient(0deg, rgba(246, 236, 193, 0.24) 0%, transparent 30%);
  pointer-events: none;
}

.summer-dog-wrap {
  position: absolute;
  left: 70%;
  top: 75%;
  z-index: 7;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.summer-dog-wrap-revealing {
  opacity: 0;
  will-change: opacity;
  animation: summerBoatReveal var(--summer-boat-intro-duration, 1.2s) cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.summer-dog {
  width: min(22vw, 290px);
  height: auto;
  will-change: transform;
  animation: sway 3.2s ease-in-out infinite;
  filter: drop-shadow(0 20px 28px rgba(55, 102, 120, 0.22));
}

.coconut {
  position: absolute;
  top: -120px;
  width: clamp(43px, 4.2vw, 67px);
  height: auto;
  z-index: 8;
  pointer-events: none;
  animation: fallDown 1.5s ease-in forwards;
  filter: drop-shadow(0 12px 18px rgba(61, 42, 21, 0.22));
}

/* ── SUMMER → AUTUMN: Golden Hour sunset overlay ── */
.sunset-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: linear-gradient(to top, rgba(255,140,0,0.45), rgba(210,105,30,0.15));
  pointer-events: none;
  opacity: 0;
}
.sunset-overlay.fade-in {
  animation: fadeIn 0.8s ease forwards;
}
.sunset-overlay.fade-out {
  animation: fadeOut 0.5s ease forwards;
}

#autumn-bg,
#autumn-glow,
#white-flash {
  position: absolute;
  inset: 0;
}

#autumn-bg {
  z-index: 1;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}

#autumn-glow {
  z-index: 2;
  background:
    radial-gradient(circle at 20% 18%, rgba(255, 237, 202, 0.22) 0%, transparent 20%),
    linear-gradient(180deg, rgba(125, 63, 34, 0.12) 0%, rgba(255, 202, 133, 0.08) 100%);
  pointer-events: none;
}

/* Ground overlay — subtle colour tint beneath the grass PNG for a soft ground shadow */
#autumn-ground-mask {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 15%;
  z-index: 65;
  background: linear-gradient(
    to top,
    rgba(190, 125, 65, 0.5) 0%,
    rgba(195, 130, 70, 0.25) 45%,
    transparent 100%
  );
  pointer-events: none;
}

/* ── WINTER: night sky + moon + aurora + stars + snow ── */

/* Scene fade-in on enter */
@keyframes winterSceneFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.winter-scene-fadein {
  animation: winterSceneFadeIn 1.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.winter-night-bg {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Deeper, richer night sky with blue-violet mid-tone */
  background: linear-gradient(
    to bottom,
    #050912 0%,
    #0b1428 18%,
    #111d3a 36%,
    #1e3259 55%,
    #1a2d4e 72%,
    #1c2f4c 100%
  );
}

/* ── Moon ── */
/*
 * Real moonlight: no hard spread rings.
 * The disc body uses radial-gradient for a soft lit-side.
 * ::before adds the atmospheric corona as a large blurred
 * radial-gradient pseudo-element — fades seamlessly into sky.
 */
.winter-moon {
  position: absolute;
  top: 8%;
  right: 22%;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  z-index: 3;
  pointer-events: none;
  /* Warm off-white disc with subtle lit-side shading */
  background: radial-gradient(
    circle at 42% 36%,
    #fffef5 0%,
    #fffce0 28%,
    #f8efb8 60%,
    #e8dc90 88%,
    #d4c870 100%
  );
  /* Pure blur-only glow — no spread radius, so no hard ring edge */
  box-shadow:
    0 0 22px  6px rgba(255, 248, 200, 0.28),
    0 0 60px 10px rgba(245, 235, 160, 0.13),
    0 0 140px 16px rgba(230, 218, 125, 0.07);
  animation: moonPulse 9s ease-in-out infinite;
}

/* Atmospheric corona — fully seamless radial-gradient, no hard boundary */
.winter-moon::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  /* 6× moon diameter ≈ 408px halo radius */
  width: 600%;
  height: 600%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(255, 248, 190, 0.20)  0%,
    rgba(252, 242, 165, 0.12) 14%,
    rgba(244, 232, 145, 0.06) 30%,
    rgba(230, 218, 128, 0.02) 52%,
    transparent 70%
  );
  pointer-events: none;
}

@keyframes moonPulse {
  0%, 100% {
    box-shadow:
      0 0 22px  6px rgba(255, 248, 200, 0.28),
      0 0 60px 10px rgba(245, 235, 160, 0.13),
      0 0 140px 16px rgba(230, 218, 125, 0.07);
    opacity: 0.90;
  }
  50% {
    box-shadow:
      0 0 30px  8px rgba(255, 250, 210, 0.34),
      0 0 80px 14px rgba(248, 240, 170, 0.16),
      0 0 180px 22px rgba(235, 222, 132, 0.09);
    opacity: 1;
  }
}

/* ── Aurora (Northern Lights) ── */
.winter-aurora {
  position: absolute;
  top: 14%;
  left: -10%;
  width: 120%;
  height: 28%;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(
      105deg,
      transparent 0%,
      rgba(64, 180, 200, 0.00) 12%,
      rgba(72, 210, 190, 0.28) 28%,
      rgba(100, 230, 200, 0.38) 40%,
      rgba(80, 200, 240, 0.24) 52%,
      rgba(130, 160, 255, 0.18) 64%,
      rgba(110, 130, 230, 0.10) 76%,
      transparent 100%
    );
  filter: blur(18px);
  mix-blend-mode: screen;
  animation: auroraFloat 14s ease-in-out infinite;
  transform-origin: center center;
  will-change: transform, opacity;
}
@keyframes auroraFloat {
  0%   { opacity: 0;    transform: translateY(0px)   scaleX(1);    }
  15%  { opacity: 0.75; transform: translateY(-6px)  scaleX(1.02); }
  38%  { opacity: 1;    transform: translateY(-14px) scaleX(1.04); }
  55%  { opacity: 0.82; transform: translateY(-8px)  scaleX(1.01); }
  72%  { opacity: 0.60; transform: translateY(-18px) scaleX(1.03); }
  88%  { opacity: 0.40; transform: translateY(-4px)  scaleX(0.99); }
  100% { opacity: 0;    transform: translateY(0px)   scaleX(1);    }
}

/* ── Stars ── */
.winter-stars {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

/* Small stars — blue-white, subtle */
.winter-star-sm {
  position: absolute;
  width: 1px;
  height: 1px;
  background: #c8d8f8;
  border-radius: 50%;
  animation: starTwinkleSm var(--star-dur, 3s) ease-in-out var(--star-delay, 0s) infinite;
}
@keyframes starTwinkleSm {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 0.85; }
}

/* Medium stars — pure white, standard */
.winter-star-md {
  position: absolute;
  width: 2px;
  height: 2px;
  background: #e8eeff;
  border-radius: 50%;
  box-shadow: 0 0 3px 1px rgba(200, 220, 255, 0.45);
  animation: starTwinkleMd var(--star-dur, 3s) ease-in-out var(--star-delay, 0s) infinite;
}
@keyframes starTwinkleMd {
  0%, 100% { opacity: 0.55; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.4); filter: brightness(1.6) drop-shadow(0 0 4px rgba(180, 210, 255, 0.8)); }
}

/* Large stars — warm gold-white, sparkle */
.winter-star-lg {
  position: absolute;
  width: 3px;
  height: 3px;
  background: #fff8e0;
  border-radius: 50%;
  box-shadow: 0 0 6px 2px rgba(255, 240, 160, 0.5);
  animation: starTwinkleLg var(--star-dur, 4.5s) ease-in-out var(--star-delay, 0s) infinite;
}
@keyframes starTwinkleLg {
  0%, 100% { opacity: 0.65; transform: scale(1);    filter: none; }
  45%      { opacity: 1;    transform: scale(1.8);  filter: brightness(2.2) drop-shadow(0 0 6px rgba(255, 230, 120, 0.9)); }
  50%      { opacity: 0.92; transform: scale(1.5);  filter: brightness(1.8) drop-shadow(0 0 4px rgba(255, 230, 120, 0.7)); }
}

/* ── Shooting star ── */
.winter-shooting-star {
  position: absolute;
  z-index: 6;
  pointer-events: none;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: #fff;
  /* tail via box-shadow and pseudo via after */
  animation: shootingStar var(--shoot-dur, 0.9s) cubic-bezier(0.22, 1, 0.36, 1) forwards;
  will-change: transform, opacity;
}
.winter-shooting-star::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 2px;
  transform: translateY(-50%);
  width: 80px;
  height: 1px;
  background: linear-gradient(to left, rgba(255, 255, 255, 0.9), transparent);
  border-radius: 1px;
}
@keyframes shootingStar {
  0%   { opacity: 0;    transform: translate(0, 0)          scale(1); }
  8%   { opacity: 1; }
  85%  { opacity: 0.7; }
  100% { opacity: 0;    transform: translate(220px, 110px)  scale(0.4); }
}

#winter-flake-canvas {
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
}

/* ── Ground moon-glow reflection ── */
.winter-ground-reflect {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 30%;
  z-index: 5;
  pointer-events: none;
  background: linear-gradient(
    to top,
    rgba(100, 145, 210, 0.18) 0%,
    rgba(120, 160, 220, 0.10) 35%,
    rgba(140, 180, 230, 0.04) 65%,
    transparent 100%
  );
}

/* ── Dog character ── */
.winter-dog-wrap {
  position: fixed;
  bottom: 0;
  left: 50%;
  z-index: 60;
  transform: translateX(-50%);
}

/* Float-in entrance */
@keyframes winterDogFloat {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(28px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}
.winter-dog-float {
  opacity: 0;
  animation: winterDogFloat 1.1s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

/* Gentle breathing after float-in completes */
@keyframes winterDogBreath {
  0%, 100% { transform: translateX(-50%) translateY(0px); }
  50%      { transform: translateX(-50%) translateY(-5px); }
}
.winter-dog-breath {
  animation: winterDogBreath 4s ease-in-out infinite;
}

.winter-dog-wrap img {
  width: min(17vw, 216px);
  height: auto;
  filter:
    drop-shadow(0 8px 20px rgba(30, 50, 100, 0.35))
    drop-shadow(0 2px 8px rgba(80, 120, 200, 0.18));
}

/* ── Advance hint ── */
.winter-hint {
  position: absolute;
  bottom: 44px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 61;
  color: rgba(180, 210, 255, 0.72);
  font-family: var(--font-zh);
  font-size: 1.1rem;
  letter-spacing: 0.22em;
  pointer-events: none;
  white-space: nowrap;
  opacity: 0;
  animation: slideUpFade 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  text-shadow: 0 0 12px rgba(100, 160, 255, 0.40);
}

#white-flash {
  z-index: 12;
  background: rgba(255, 251, 246, 0.98);
  pointer-events: none;
  animation: whiteFlash 0.8s ease-in-out forwards;
}

/* SPRING → SUMMER: spring scene fades out before transition */
@keyframes springFadeOut {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.02); }
}

.spring-fade-out {
  animation: springFadeOut 0.8s ease-in forwards;
  pointer-events: none;
}

/* SUMMER: multi-layer SVG waves along diagonal beach shoreline */
/* bg-summer.png coastline (image-native 2752×1536):
   Upper coast: (853,795)→(1079.5,845.5)→(1225.5,845.5)→(1407,896)→(1684,941)
   Bay fold:    (1407,971.5)→(1326.5,1006.5)→(1145,1006.5)→(984,1042)→(853,1127.5)
   Lower coast: (903.5,1178)→(1225.5,1248.5)→(1407,1319)→(1850,1369)→(2056.5,1460)→(2348.5,1535.5)→(2751.5,1510)
   SVG uses preserveAspectRatio="xMidYMid slice" to match CSS background-size:cover */
#summer-waves {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
}

.wave-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.wave-layer {
  transform-origin: center center;
  will-change: transform, opacity;
}

/* Ocean surface base — receives feTurbulence displacement filter */
.wave-surface {
  fill: rgba(131, 197, 215, 0.28);
  /* filter applied via SVG attribute — no CSS filter needed */
}

/* Furthest wave — faint blue shimmer */
.wave-very-far {
  fill: rgba(143, 206, 219, 0.14);
  animation: waveSurge-very-far 3.2s ease-in-out infinite;
}

/* Furthest wave — faint blue shimmer */
.wave-far {
  fill: rgba(160, 218, 228, 0.22);
  animation: waveSurge-far 3.2s ease-in-out infinite;
}

/* Mid wave — white translucent surf line */
.wave-mid {
  fill: rgba(255, 255, 255, 0.38);
  animation: waveSurge-mid 3.2s ease-in-out infinite;
}

/* Nearest wave — bright foam crest */
.wave-near {
  fill: rgba(255, 255, 255, 0.55);
  animation: waveSurge-near 3.2s ease-in-out infinite;
}

@keyframes summerBoatReveal {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Waves surge toward shore: roughly perpendicular to the main coastline */
@keyframes waveSurge-very-far {
  0%, 100% { transform: translate(0, 0);        opacity: 0.12; }
  32%      { transform: translate(-2px, +7px);  opacity: 0.22; }
  50%      { transform: translate(-3px, +9px);  opacity: 0.26; }
  74%      { transform: translate(-1px, +4px);  opacity: 0.16; }
}

@keyframes waveSurge-far {
  0%, 100% { transform: translate(0, 0);         opacity: 0.18; }
  35%      { transform: translate(-4px, +12px);  opacity: 0.4;  }
  55%      { transform: translate(-5px, +16px);  opacity: 0.3;  }
}

@keyframes waveSurge-mid {
  0%, 100% { transform: translate(0, 0);         opacity: 0.28; }
  25%      { transform: translate(-5px, +18px);  opacity: 0.65; }
  45%      { transform: translate(-7px, +22px);  opacity: 0.5;  }
  70%      { transform: translate(-3px, +10px);  opacity: 0.35; }
}

@keyframes waveSurge-near {
  0%       { transform: translate(0, 0) scaleX(1);             opacity: 0.35; }
  30%      { transform: translate(-7px, +23px) scaleX(1.05);   opacity: 0.85; }
  50%      { transform: translate(-9px, +28px) scaleX(1.03);   opacity: 0.6;  }
  75%      { transform: translate(-5px, +14px) scaleX(1.01);   opacity: 0.45; }
  100%     { transform: translate(0, 0) scaleX(1);             opacity: 0.35; }
}

/* Foam particles — small dots that appear/fade along shoreline */
.foam-particles {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.foam-dot {
  position: absolute;
  /* width / height injected by JS per-dot for size variation */
  min-width: 3px;
  min-height: 3px;
  background: rgba(255, 255, 255, 0.75);
  border-radius: 50%;
  animation: foamBubble var(--foam-dur, 3s) ease-in-out var(--foam-delay, 0s) infinite;
}

@keyframes foamBubble {
  0%, 100% { opacity: 0;   transform: scale(0.4); }
  25%      { opacity: 0.85; transform: scale(1.3); }
  60%      { opacity: 0.45; transform: scale(0.9); }
}

@keyframes slideUpFade {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes dogBreath {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}

@keyframes petalFloat {
  0%   { opacity: 0; transform: translateY(10px); }
  60%  { opacity: var(--petal-opacity, 0.36); transform: translateY(-4px); }
  100% { opacity: var(--petal-opacity, 0.36); transform: translateY(-8px); }
}

@keyframes sparkAppear {
  from { opacity: 0; transform: scale(0.3) rotate(-15deg); }
  to   { opacity: 1; transform: scale(1) rotate(0deg); }
}

@keyframes sparkPulse {
  0%, 100% { opacity: 0.72; transform: scale(1) rotate(0deg); }
  50%      { opacity: 1;    transform: scale(1.35) rotate(18deg); }
}

@keyframes hintPulse {
  0%, 100% { box-shadow: 0 16px 32px oklch(54% 0.06 52 / 0.16); transform: translateY(0); }
  50%      { box-shadow: 0 20px 40px oklch(54% 0.06 52 / 0.28); transform: translateY(-2px); }
}

@keyframes cakeWarmBloom {
  0%   { opacity: 0; transform: scale(0.96); }
  45%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.04); }
}

@keyframes cakeSuccessNote {
  0%   { opacity: 0.82; transform: translateX(-50%) scale(0.96); }
  60%  { opacity: 1; transform: translateX(-50%) scale(1.03); }
  100% { opacity: 1; transform: translateX(-50%) scale(1); }
}

@keyframes wishAppear {
  0% {
    opacity: 0;
    transform: translateX(-50%) translateY(12px) scale(0.94);
  }
  60% {
    opacity: 1;
    transform: translateX(-50%) translateY(-2px) scale(1.04);
  }
  100% {
    opacity: 1;
    transform: translateX(-50%) translateY(0) scale(1);
  }
}

@keyframes fireworkWarmVeil {
  0%   { opacity: 0; }
  100% { opacity: 0.94; }
}

@keyframes envelopeInvite {
  0%, 100% { transform: translateY(0) rotate(0deg) scale(1); }
  28%      { transform: translateY(-4px) rotate(-3deg) scale(1.03); }
  52%      { transform: translateY(-1px) rotate(2deg) scale(1.01); }
  76%      { transform: translateY(-6px) rotate(-1deg) scale(1.05); }
}

@keyframes envelopeOpen {
  0%   { transform: scale(1) rotate(0deg); opacity: 1; }
  55%  { transform: scale(1.08) rotate(-5deg); opacity: 1; }
  100% { transform: scale(0.94) rotate(3deg); opacity: 0; }
}

@keyframes paperBreath {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(-2px) scale(1.002); }
}

@keyframes pawSpark {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.2);
  }
  28% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--spark-x, 0px)), calc(-50% + var(--spark-y, -52px))) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  #landing-title,
  #landing-dog,
  .landing-corner,
  .landing-petal,
  .landing-spark,
  .gallery-card,
  #gallery-hint,
  .popup-cute,
  .sunset-overlay,
  .winter-star-sm,
  .winter-star-md,
  .winter-star-lg,
  .winter-dog-float,
  .winter-dog-breath,
  .winter-aurora,
  .winter-moon,
  .winter-shooting-star,
  .winter-scene-fadein,
  .spring-panel,
  #spring-hint,
  .spring-dog,
  .mango-jump-dog,
  .summer-dog,
  .summer-dog-wrap-revealing,
  .coconut,
  #white-flash,
  .summer-reveal,
  .spring-fade-out,
  .wave-very-far,
  .wave-far,
  .wave-mid,
  .wave-near,
  .foam-dot,
  .cake-scene,
  .cake-instruction,
  #btn-wish,
  .firework-scene,
  .firework-dog-wrap,
  .firework-star,
  .firework-bokeh,
  .finale-scene,
  .finale-text,
  #envelope-icon,
  .letter-stage,
  .letter-card,
  .envelope-scene,
  .letter-paper--full,
  .letter-paw-sign,
  .letter-paw-spark,
  #btn-back {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ===== Reusable textured background noise ===== */
/* Add class 'textured-bg' to #app; the ::before provides a subtle paper grain */
.textured-bg {
  position: relative;
}
.textured-bg::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='bg'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23bg)' opacity='0.6'/%3E%3C/svg%3E");
  opacity: 0.045;
  mix-blend-mode: multiply;
}

/* ===== LANDING State ===== */

/* Corner watercolour blobs — fills empty quadrants with soft tinted glow */
#landing-corner-wrap {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

.landing-corner {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  animation: fadeIn 2s ease 0.05s forwards;
}

.landing-corner[data-pos="tl"] {
  top: 0; left: 0;
  width: 320px; height: 240px;
  background: radial-gradient(ellipse at 0% 0%, oklch(89% 0.08 17 / 0.34) 0%, transparent 68%);
  filter: blur(36px);
}

.landing-corner[data-pos="tr"] {
  top: 0; right: 0;
  width: 300px; height: 220px;
  background: radial-gradient(ellipse at 100% 0%, oklch(88% 0.10 84 / 0.28) 0%, transparent 68%);
  filter: blur(40px);
}

.landing-corner[data-pos="bl"] {
  bottom: 0; left: 0;
  width: 280px; height: 200px;
  background: radial-gradient(ellipse at 0% 100%, oklch(91% 0.06 22 / 0.22) 0%, transparent 65%);
  filter: blur(38px);
}

.landing-corner[data-pos="br"] {
  bottom: 0; right: 0;
  width: 260px; height: 200px;
  background: radial-gradient(ellipse at 100% 100%, oklch(90% 0.06 195 / 0.20) 0%, transparent 65%);
  filter: blur(36px);
}

/* Floating soft petals — mid-layer filler */
#landing-petals {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

.landing-petal {
  position: absolute;
  left: var(--petal-x);
  top: var(--petal-y);
  width: var(--petal-size);
  height: var(--petal-size);
  border-radius: 50%;
  background: var(--petal-color);
  filter: blur(2.5px);
  opacity: 0;
  animation: petalFloat var(--petal-dur, 5s) var(--ease-out-soft) var(--petal-delay, 0s) forwards;
}

/* ✦ Spark symbols — orbiting the title */
#landing-sparks {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
}

.landing-spark {
  position: absolute;
  left: var(--spark-x);
  top: var(--spark-y);
  font-size: var(--spark-size, 1rem);
  color: var(--spark-color);
  line-height: 1;
  opacity: 0;
  animation:
    sparkAppear 0.5s var(--ease-out-soft) var(--spark-delay, 0.8s) forwards,
    sparkPulse  3.2s ease-in-out var(--spark-delay, 0.8s) infinite;
}

/* Preload progress text */
#preload-text {
  font-family: var(--font-zh);
  font-size: 1.8rem;
  color: #7a6a5a;
  letter-spacing: 0.08em;
}

/* "Happy Birthday" heading — centred above the start button */
#landing-title {
  position: absolute;
  top: 40%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-en);
  font-size: clamp(4.6rem, 7vw, 7rem);
  font-weight: 700;
  color: oklch(58% 0.10 22);
  letter-spacing: 0.02em;
  white-space: nowrap;
  text-shadow:
    0 2px 14px oklch(46% 0.08 22 / 0.22),
    0 0px 34px oklch(89% 0.04 78 / 0.28);
  opacity: 0;
  animation: fadeIn 1.1s ease 0.1s forwards;
  pointer-events: auto;
  cursor: default;
  z-index: 2;
  transition: color 0.35s ease, text-shadow 0.35s ease;
}

#landing-title:hover {
  color: oklch(52% 0.12 20);
  text-shadow:
    0 2px 20px oklch(42% 0.09 20 / 0.28),
    0 0   42px oklch(88% 0.05 78 / 0.34);
}

/* Start button — centred in #app via absolute positioning */
#btn-start {
  position: absolute;
  top: 67%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-en);
  font-size: 1.5rem;
  padding: 0.75rem 2.8rem;
  z-index: 2;
  border: 1.25px solid oklch(68% 0.07 28 / 0.34);
  border-radius: 999px;
  background: linear-gradient(180deg, oklch(97.8% 0.018 78 / 0.98), oklch(92.8% 0.032 28 / 0.94));
  color: oklch(44% 0.08 24);
  cursor: pointer;
  box-shadow:
    0 2px 8px oklch(42% 0.05 28 / 0.10),
    0 8px 20px oklch(42% 0.05 28 / 0.14),
    inset 0 1px 0 oklch(99% 0.01 80 / 0.84);
  transition:
    transform 0.28s cubic-bezier(0.34,1.56,0.64,1),
    box-shadow 0.28s ease,
    background 0.25s ease;
}

#btn-start:hover {
  background: linear-gradient(180deg, oklch(98.8% 0.016 80), oklch(94.6% 0.040 24));
  transform: translate(-50%, -56%) scale(1.04);
  box-shadow:
    0 4px 12px oklch(42% 0.05 28 / 0.14),
    0 12px 32px oklch(42% 0.06 28 / 0.18),
    0 0 0 1px oklch(72% 0.09 24 / 0.22),
    inset 0 1px 0 oklch(100% 0 0 / 0.90);
}

#btn-start:active {
  transform: translate(-50%, -56%) scale(0.96);
  box-shadow:
    0 1px 4px oklch(42% 0.05 28 / 0.08),
    0 2px 8px oklch(42% 0.05 28 / 0.10),
    inset 0 1px 0 oklch(99% 0.01 80 / 0.72);
  transition-duration: 0.1s;
}

/* Dog illustration — bottom-left corner, static pose */
#landing-dog {
  position: absolute;
  bottom: 36px;
  left: 56px;
  width: 220px;
  height: auto;
  pointer-events: none;
  opacity: 0;
  z-index: 4;
  animation: fadeIn 1s ease 0.4s forwards, dogBreath 3.5s ease-in-out 1.4s infinite;
}

/* ===== GALLERY State ===== */

#gallery-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.2rem;
  width: 100%;
  max-width: 1300px;
  padding: 0 2rem;
}

.gallery-row {
  display: flex;
  justify-content: center;
  gap: 1.2rem;
}

.gallery-card {
  width: clamp(150px, 19vw, 240px);
  height: clamp(120px, 15vw, 190px);
  border-radius: 14px;
  background: linear-gradient(180deg, var(--surface-paper) 0%, var(--surface-paper-strong) 100%);
  border: 1px solid oklch(76% 0.04 72 / 0.38);
  overflow: hidden;
  cursor: pointer;
  flex-shrink: 0;
  opacity: 0;
  animation: slideUpFade 0.65s var(--ease-out-soft) forwards;
  box-shadow: 0 10px 26px oklch(48% 0.04 52 / 0.12);
  transition: transform 0.25s var(--ease-out-soft), box-shadow 0.25s ease, border-color 0.25s ease, background 0.25s ease;
}

.gallery-card:hover {
  transform: translateY(-5px) scale(1.02);
  border-color: oklch(72% 0.12 83 / 0.42);
  box-shadow:
    0 14px 34px oklch(46% 0.07 52 / 0.16),
    0 0 0 1px oklch(84% 0.10 84 / 0.18);
}

/* Hint text below gallery grid */
#gallery-hint {
  margin-top: 2.4rem;
  font-family: var(--font-zh);
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--ink-muted);
  letter-spacing: 0.12em;
  opacity: 0;
  animation: fadeIn 0.65s ease 0s forwards;
  pointer-events: none;
}

/* ===== POPUP_CUTE State ===== */

/* Plain text — no box, click to advance */
.popup-cute {
  font-family: var(--font-zh);
  font-size: 2.6rem;
  color: var(--accent-rose);
  letter-spacing: 0.16em;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  opacity: 0;
  animation: fadeIn 1.5s ease forwards;
  text-shadow:
    0 2px 14px oklch(58% 0.13 18 / 0.38),
    0 0   32px oklch(88% 0.05 83 / 0.35);
  transition: color 0.25s ease, text-shadow 0.25s ease;
}

.popup-cute:hover {
  color: var(--accent-rose-strong);
  text-shadow:
    0 4px 20px oklch(56% 0.16 16 / 0.56),
    0 0   40px oklch(90% 0.06 84 / 0.46);
}

.popup-cute:active {
  opacity: 0.7;
  transform: scale(0.97);
  transition-duration: 0.1s;
}

/* ===== CAKE State ===== */

.cake-scene {
  position: absolute;
  inset: 0;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  overflow: hidden;
  animation: fadeIn 0.8s ease forwards;
}

.cake-scene::before {
  content: '';
  position: absolute;
  inset: -8%;
  background:
    radial-gradient(circle at 50% 72%, rgba(255, 216, 146, 0.22), transparent 28%),
    radial-gradient(circle at 56% 48%, rgba(255, 247, 223, 0.18), transparent 20%);
  opacity: 0;
  pointer-events: none;
}

.cake-scene-celebrate::before {
  animation: cakeWarmBloom 0.95s var(--ease-out-strong) forwards;
}

.cake-scene::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(88, 64, 44, 0.10), rgba(252, 244, 228, 0.08) 32%, rgba(255, 241, 216, 0.05)),
    radial-gradient(circle at 82% 18%, rgba(255, 226, 168, 0.18), transparent 22%),
    radial-gradient(circle at 12% 20%, rgba(255, 243, 214, 0.12), transparent 18%);
  pointer-events: none;
}

.cake-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: opacity 0.35s var(--ease-out-soft), transform 0.35s var(--ease-out-soft);
}

.cake-instruction {
  position: absolute;
  top: 44px;
  left: 50%;
  transform: translateX(-50%);
  min-width: 256px;
  padding: 0.52rem 1.28rem;
  border-radius: 999px;
  background:
    linear-gradient(180deg, oklch(98% 0.014 82 / 0.96), oklch(94% 0.024 77 / 0.88));
  border: 1px solid oklch(72% 0.05 72 / 0.24);
  color: var(--ink-warm);
  font-family: var(--font-zh);
  font-size: 1.48rem;
  letter-spacing: 0.08em;
  text-align: center;
  box-shadow:
    0 10px 24px oklch(46% 0.05 52 / 0.10),
    inset 0 1px 0 oklch(100% 0 0 / 0.80);
  opacity: 0;
  animation: fadeIn 0.9s ease 0.2s forwards;
}

.cake-instruction-success {
  background:
    linear-gradient(180deg, oklch(98% 0.014 82), oklch(95% 0.028 145));
  color: oklch(34% 0.05 58);
  box-shadow:
    0 14px 28px oklch(46% 0.05 52 / 0.14),
    inset 0 1px 0 oklch(100% 0 0 / 0.92),
    0 0 0 1px oklch(72% 0.09 145 / 0.22);
  animation: cakeSuccessNote 0.58s var(--ease-out-strong) forwards;
}

.cake-overlay-ready {
  opacity: 0;
  transform: translateY(-12px);
}

#btn-wish {
  position: absolute;
  left: 50%;
  bottom: 60px;
  transform: translateX(-50%);
  padding: 0.72rem 2.24rem 0.68rem;
  border: 1px solid oklch(74% 0.10 83 / 0.32);
  border-radius: 999px;
  background:
    linear-gradient(180deg, oklch(98% 0.014 82 / 0.98), oklch(92% 0.032 84 / 0.94));
  color: var(--ink-warm);
  font-family: var(--font-zh);
  font-size: 1.6rem;
  letter-spacing: 0.12em;
  cursor: pointer;
  box-shadow:
    0 14px 32px oklch(46% 0.05 52 / 0.18),
    inset 0 1px 0 oklch(100% 0 0 / 0.90);
  opacity: 0;
  animation: wishAppear 0.72s var(--ease-out-strong) forwards;
  transition: transform 0.28s var(--ease-out-soft), box-shadow 0.28s ease, background 0.28s ease;
  pointer-events: auto;
  z-index: 60;
}

#btn-wish:hover:not(:disabled) {
  transform: translateX(-50%) translateY(-4px);
  background:
    linear-gradient(180deg, oklch(99% 0.012 82), oklch(95% 0.03 84));
  box-shadow:
    0 18px 40px oklch(46% 0.05 52 / 0.22),
    0 0 0 1px oklch(78% 0.12 83 / 0.18),
    inset 0 1px 0 oklch(100% 0 0 / 0.95);
}

#btn-wish:disabled {
  cursor: default;
  opacity: 0.72;
  transform: translateX(-50%);
}

#btn-wish:active:not(:disabled) {
  transform: translateX(-50%) scale(0.95);
  box-shadow:
    0 8px 16px oklch(46% 0.05 52 / 0.14),
    inset 0 1px 0 oklch(100% 0 0 / 0.85);
  transition-duration: 0.1s;
}

/* ===== FIREWORK_TEXT State ===== */

.firework-scene {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 45%, transparent 40%, oklch(13% 0.03 53 / 0.56) 100%),
    radial-gradient(circle at 50% 18%, oklch(24% 0.05 52 / 0.34), transparent 22%),
    radial-gradient(circle at 50% 70%, oklch(76% 0.11 83 / 0.10), transparent 20%),
    linear-gradient(180deg, oklch(19% 0.03 53) 0%, oklch(15% 0.025 53) 50%, oklch(11% 0.022 53) 100%);
  overflow: hidden;
  animation: fadeIn 0.5s ease forwards;
}

.firework-scene-ready {
  cursor: pointer;
}

.firework-scene::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 50% 82%, oklch(83% 0.12 84 / 0.12), transparent 18%),
    linear-gradient(180deg, transparent, oklch(13% 0.03 53 / 0.24));
  pointer-events: none;
}

.firework-scene::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 50% 56%, oklch(94% 0.05 82 / 0.58), transparent 32%),
    linear-gradient(180deg, oklch(98% 0.012 82 / 0.08), oklch(93% 0.03 81 / 0.82));
  opacity: 0;
  pointer-events: none;
}

.firework-scene-leaving::after {
  animation: fireworkWarmVeil 0.36s var(--ease-out-strong) forwards;
}

.firework-ground-glow {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 18%;
  background: linear-gradient(
    to top,
    oklch(82% 0.13 83 / 0.10) 0%,
    oklch(90% 0.09 84 / 0.05) 40%,
    transparent 100%
  );
  mix-blend-mode: screen;
  pointer-events: none;
  z-index: 1;
  opacity: 0.82;
  transform-origin: center bottom;
  transition: opacity 0.55s var(--ease-out-soft), transform 0.55s var(--ease-out-soft);
}

.firework-scene-settled .firework-ground-glow {
  opacity: 1;
  transform: scaleY(1.08);
}

.firework-star {
  position: absolute;
  width: 2px;
  height: 2px;
  background: var(--accent-gold-soft);
  border-radius: 50%;
  opacity: 0;
  box-shadow: 0 0 3px 1px oklch(97% 0.02 82 / 0.40);
  animation: starTwinkle 3s ease-in-out infinite;
  pointer-events: none;
}

.firework-bokeh {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  animation:
    fadeIn 2s ease forwards,
    bokehDrift 18s ease-in-out infinite;
}

@keyframes bokehDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33%      { transform: translate(12px, -8px) scale(1.05); }
  66%      { transform: translate(-8px, 6px) scale(0.97); }
}

.firework-dog-wrap {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: min(26vw, 280px);
  transform: translateX(-50%);
  opacity: 0;
  animation: fadeIn 0.9s ease 0.18s forwards;
}

.firework-dog {
  display: block;
  width: 100%;
  height: auto;
  filter:
    drop-shadow(0 10px 20px oklch(10% 0.02 52 / 0.38))
    drop-shadow(0 -6px 24px oklch(83% 0.13 84 / 0.12));
}

/* ── FIREWORK_TEXT: circular bloom from text centre when particles settle ── */
@keyframes settleBurst {
  0%   { transform: translate(-50%, -50%) scale(0.1); opacity: 0.95; box-shadow: 0 0 0 0 rgba(255, 228, 80, 0.7); }
  55%  { transform: translate(-50%, -50%) scale(1);   opacity: 0.55; box-shadow: 0 0 48px 24px rgba(255, 228, 80, 0.28); }
  100% { transform: translate(-50%, -50%) scale(1.8); opacity: 0;    box-shadow: 0 0 80px 48px rgba(255, 228, 80, 0); }
}

.firework-settle-burst {
  position: absolute;
  left: 50%;
  top: 40%;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(255, 235, 100, 0.85);
  pointer-events: none;
  z-index: 22;
  animation: settleBurst 0.7s ease-out forwards;
}

/* ── FIREWORK_TEXT: ripple ring from text centre ── */
@keyframes pulseRing {
  0%   { transform: translate(-50%, -50%) scale(1);  opacity: 0.72; }
  100% { transform: translate(-50%, -50%) scale(55); opacity: 0; }
}

.firework-pulse-ring {
  position: absolute;
  left: 50%;
  top: 40%;
  width: 8px;
  height: 8px;
  border: 2px solid rgba(255, 220, 100, 0.8);
  border-radius: 50%;
  pointer-events: none;
  z-index: 21;
  animation: pulseRing 1.0s ease-out forwards;
}

/* ── FIREWORK_TEXT: confetti rain ── */
@keyframes confettiFall {
  0%   { transform: translateY(-20px) rotate(var(--confetti-rot, 0deg)); opacity: 1; }
  75%  { opacity: 0.85; }
  100% { transform: translateY(110vh) rotate(calc(var(--confetti-rot, 0deg) + 540deg)); opacity: 0; }
}

.firework-confetti {
  position: absolute;
  top: -20px;
  background: var(--confetti-color, #ffd250);
  border-radius: 2px;
  pointer-events: none;
  z-index: 20;
}

/* ── FIREWORK_TEXT: dog celebrate bounce (must come after .firework-dog-wrap) ── */
@keyframes dogReact {
  0%   { transform: translateX(-50%) scale(1.00); }
  20%  { transform: translateX(-50%) scale(0.88); }
  52%  { transform: translateX(-50%) scale(1.16); }
  78%  { transform: translateX(-50%) scale(1.03); }
  100% { transform: translateX(-50%) scale(1.00); }
}

.firework-dog-react {
  animation: dogReact 0.72s ease-out forwards;
}

/* FINALE→LETTER: envelope card expands from bottom-right corner to full screen */
@keyframes expandFromCorner {
  from {
    transform-origin: bottom right;
    transform: scale(0.05);
    opacity: 0.7;
  }
  to {
    transform-origin: bottom right;
    transform: scale(1);
    opacity: 1;
  }
}

/* ===== FINALE State ===== */

.finale-scene {
  position: absolute;
  inset: 0;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  animation: fadeIn 1.5s ease forwards;
}

.finale-text {
  position: absolute;
  right: 10%;
  top: 35%;
  transform: translate3d(var(--sheen-drift-x, 0px), calc(-50% + var(--sheen-drift-y, 0px) + var(--sheen-lift, 0px)), 0) rotate(var(--sheen-tilt, 0deg));
  display: inline-block;
  font-family: var(--font-en);
  font-size: clamp(3.5rem, 5vw, 7rem);
  line-height: 1;
  color: oklch(97.6% 0.02 83);
  text-shadow:
    0 2px 24px oklch(10% 0.02 52 / 0.55),
    0 0 48px oklch(84% 0.07 84 / 0.34);
  letter-spacing: 0.06em;
  margin: 0;
  opacity: 0;
  animation: fadeIn 1.2s ease 1s forwards;
  pointer-events: none;
  white-space: nowrap;
  --sheen-x: 50%;
  --sheen-y: 50%;
  --sheen-opacity: 0;
  --sheen-scale: 1;
  --sheen-drift-x: 0px;
  --sheen-drift-y: 0px;
  --sheen-lift: 0px;
  --sheen-tilt: 0deg;
  transition: transform 0.18s ease, text-shadow 0.18s ease;
}

.finale-text::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  display: block;
  color: oklch(99% 0.015 84);
  text-shadow:
    0 0 16px oklch(98% 0.02 84 / 0.35),
    0 0 42px oklch(83% 0.12 84 / 0.48),
    0 0 70px oklch(98% 0.015 84 / 0.24);
  opacity: var(--sheen-opacity);
  transform: scale(var(--sheen-scale));
  transform-origin: center;
  transition: opacity 0.18s ease, transform 0.18s ease;
  mask-image: radial-gradient(circle 130px at var(--sheen-x) var(--sheen-y), rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.94) 18%, rgba(0, 0, 0, 0.48) 42%, transparent 72%);
  -webkit-mask-image: radial-gradient(circle 130px at var(--sheen-x) var(--sheen-y), rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.94) 18%, rgba(0, 0, 0, 0.48) 42%, transparent 72%);
}

/* ── FINALE: individual character reveal ── */
@keyframes letterReveal {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.letter-char {
  display: inline;
  opacity: 0;
  animation: letterReveal 0.5s ease-out forwards;
}

/* ── FINALE: love shimmer pulse after all letters appear ── */
@keyframes shimmerPulse {
  0%, 100% { filter: drop-shadow(0 0 6px rgba(255, 220, 100, 0.38)); }
  50%       { filter: drop-shadow(0 0 22px rgba(255, 220, 100, 0.68)) drop-shadow(0 0 36px rgba(255, 200, 70, 0.30)); }
}

.love-shimmer {
  animation: shimmerPulse 2.8s ease-in-out infinite;
}

#envelope-icon {
  position: fixed;
  right: 32px;
  bottom: 32px;
  width: min(10vw, 96px);
  cursor: pointer;
  opacity: 0;
  animation:
    fadeIn 0.8s ease 1.4s forwards,
    envelopeInvite 3.2s var(--ease-out-soft) 2.4s 2;
  filter: drop-shadow(0 6px 16px oklch(10% 0.02 52 / 0.40));
  z-index: 80;
  transform-origin: 50% 82%;
  transition: transform 0.25s ease, filter 0.25s ease;
}

#envelope-icon:hover {
  transform: translateY(-7px) scale(1.10) rotate(-3deg);
  filter: drop-shadow(0 12px 28px oklch(10% 0.02 52 / 0.52));
}

#envelope-icon.is-opening {
  animation: envelopeOpen 0.24s var(--ease-out-strong) forwards;
}

/* ===== LETTER State ===== */

/* Full-screen stage container */
.letter-stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  animation: fadeIn 0.4s ease forwards;
}

/* Blurred backdrop — foxDogLove from FINALE, darkened */
.letter-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  filter: blur(18px) brightness(0.62);
  transform: scale(1.08); /* prevent blur edge bleed */
  z-index: 0;
}

/* Extra dark scrim on the blurred bg */
.letter-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: oklch(10% 0.02 50 / 0.30);
  z-index: 1;
}

/* ── Envelope scene ────────────────────────────────────────────────────────── */
/* Positioned at screen center; animation brings it from the captured FINALE corner */
.envelope-scene {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 5;
  pointer-events: none;
  animation: envelopeArrive 0.70s cubic-bezier(0.22, 1.12, 0.36, 1) forwards;
  will-change: transform, opacity;
}

.envelope-scene.is-dismissing {
  animation: envelopeDismiss 0.40s ease-in forwards;
}

/* Perspective wrapper */
.envelope-wrap {
  position: relative;
  width: clamp(180px, 24vw, 300px);
  perspective: 700px;
}

/* The actual envelope PNG */
.envelope-body {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 16px 36px oklch(6% 0.02 50 / 0.55));
}

/* CSS-only flap overlay — a warm-toned triangle covering the envelope's top portion */
.envelope-flap {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 46%;
  clip-path: polygon(0 0, 100% 0, 50% 90%);
  background: oklch(91% 0.025 76 / 0.88);
  transform-origin: top center;
  transform-style: preserve-3d;
  backface-visibility: hidden;
}

.envelope-flap.is-opening {
  animation: envelopeFlapOpen 0.50s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

/* ── Letter card ───────────────────────────────────────────────────────────── */
.letter-card {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(480px, 62vw, 680px);
  opacity: 0;
  pointer-events: none;
  z-index: 10;
  filter:
    drop-shadow(0 24px 60px oklch(8% 0.02 52 / 0.48))
    drop-shadow(0 4px 12px oklch(8% 0.02 52 / 0.28));
}

.letter-card.is-revealed {
  animation: letterCardReveal 0.58s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  pointer-events: auto;
}

/* Override paper dimensions inside the card — card controls the max-height */
.letter-card .letter-paper--full {
  height: auto;
  max-height: 78vh;
  border-radius: 2px;
  animation: none;
}

.letter-card.is-revealed .letter-paper--full {
  animation: paperBreath 7s ease-in-out 1.2s infinite;
}

/* Back button visibility driven by card state */
.letter-card.is-revealed #btn-back {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

/* ── LETTER keyframes ──────────────────────────────────────────────────────── */

/* Envelope travels from the captured FINALE icon position to screen center */
@keyframes envelopeArrive {
  from {
    transform: translate(
      calc(-50% + var(--env-start-x, 38vw)),
      calc(-50% + var(--env-start-y, 36vh))
    ) scale(0.24);
    opacity: 0;
  }
  55% { opacity: 1; }
  to {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
}

/* Flap rotates open on the X-axis with 3D perspective */
@keyframes envelopeFlapOpen {
  from { transform: rotateX(0deg); }
  to   { transform: rotateX(-174deg); }
}

/* Envelope fades and floats up as the letter card takes focus */
@keyframes envelopeDismiss {
  from { transform: translate(-50%, -50%) scale(1);    opacity: 1; }
  to   { transform: translate(-50%, -53%) scale(0.80); opacity: 0; }
}

/* Letter card rises from the envelope position and expands to full size */
@keyframes letterCardReveal {
  from {
    transform: translate(-50%, -43%) scale(0.88);
    opacity: 0;
  }
  to {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
}

/* ── Reusable letter paper components ──────────────────────────────────────── */

/* Layout modifier — added alongside .letter-paper base styles */
/* padding aligns to the 42px ruled-line grid: top = 42px, left = 77px (margin at 64px + gutter) */
.letter-paper--full {
  width: 100%;
  height: 78vh;
  max-height: 78vh;
  padding: 42px 3rem 42px 77px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
  transform-origin: center top;
}

/* Dog-ear fold — top-right corner */
.letter-paper--full::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 44px;
  height: 44px;
  background: linear-gradient(
    225deg,
    oklch(89% 0.02 76 / 0.96) 0%,
    oklch(93% 0.02 78 / 0.90) 46%,
    oklch(98% 0.010 82 / 0.90) 49%,
    oklch(96% 0.015 80 / 0.85) 50%,
    transparent 50.5%
  );
  box-shadow:
    -3px 3px 8px oklch(35% 0.04 53 / 0.18),
    -1px 1px 2px oklch(35% 0.04 53 / 0.12);
  z-index: 2;
  pointer-events: none;
}

/* Decorative flourish above letter content — occupies one full 42px grid row */
.letter-ornament-top {
  flex-shrink: 0;
  width: 60%;
  max-width: 260px;
  height: 42px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  position: relative;
  z-index: 1;
  background:
    linear-gradient(
      90deg,
      transparent 0%,
      oklch(74% 0.05 75 / 0.35) 20%,
      oklch(74% 0.05 75 / 0.50) 50%,
      oklch(74% 0.05 75 / 0.35) 80%,
      transparent 100%
    );
  background-size: 100% 2px;
  background-repeat: no-repeat;
  background-position: center;
}

/* Small diamond center accent */
.letter-ornament-top::before {
  content: '✦ ◇ ✦';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.75rem;
  color: oklch(70% 0.06 76 / 0.90);
  background: var(--surface-paper);
  padding: 0 10px;
  line-height: 1;
  letter-spacing: 0.3em;
}

/* Paw print signature — top margin = one full 42px grid row */
.letter-paw-sign {
  flex-shrink: 0;
  text-align: center;
  font-size: 1.6rem;
  width: max-content;
  align-self: center;
  margin: 42px 0 0;
  opacity: 0.65;
  z-index: 1;
  filter: grayscale(0.22);
  cursor: pointer;
  position: relative;
  transition: transform 0.26s var(--ease-out-soft), opacity 0.26s ease, filter 0.26s ease;
}

.letter-paw-sign-blessing {
  transform: translateY(-2px) scale(1.08);
  opacity: 0.82;
  filter:
    grayscale(0)
    drop-shadow(0 0 10px oklch(83% 0.12 84 / 0.55));
}

.letter-paw-spark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--spark-size, 6px);
  height: var(--spark-size, 6px);
  border-radius: 999px;
  background: radial-gradient(circle, oklch(99% 0.014 83 / 0.96), oklch(83% 0.12 84 / 0.72) 44%, oklch(83% 0.12 84 / 0) 72%);
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.2);
  animation: pawSpark 0.86s var(--ease-out-strong) var(--spark-delay, 0s) forwards;
}

.letter-content {
  flex: 1;
  overflow-y: auto;
  white-space: pre-wrap;
  font-family: var(--font-zh);
  font-size: 2.1rem;
  line-height: 42px;
  letter-spacing: 0.08em;
  color: var(--ink-warm);
  padding-right: 0.4rem;
  margin: 0;
  position: relative;
  z-index: 1;
  text-shadow:
    0px 0px 1.5px oklch(30% 0.03 52 / 0.20),
    0.5px 1px 1px oklch(100% 0 0 / 0.72),
    0 2px 4px oklch(35% 0.03 52 / 0.08);
}

/* Vintage scrollbar styling */
.letter-content::-webkit-scrollbar {
  width: 8px;
}
.letter-content::-webkit-scrollbar-track {
  background: oklch(86% 0.03 76 / 0.30);
  border-radius: 4px;
}
.letter-content::-webkit-scrollbar-thumb {
  background: oklch(74% 0.08 78 / 0.45);
  border-radius: 4px;
}
.letter-content::-webkit-scrollbar-thumb:hover {
  background: oklch(74% 0.10 78 / 0.65);
}

#btn-back {
  flex-shrink: 0;
  align-self: center;
  margin-top: 21px;
  padding: 0.7rem 2.4rem;
  border: 1.5px solid oklch(72% 0.07 76 / 0.55);
  border-radius: 999px;
  background: linear-gradient(180deg, oklch(98% 0.014 82 / 0.96), oklch(93% 0.028 84 / 0.90));
  color: var(--ink-warm);
  font-family: var(--font-zh);
  font-size: 1.6rem;
  letter-spacing: 0.10em;
  cursor: pointer;
  box-shadow: 0 6px 20px oklch(39% 0.05 53 / 0.14);
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: opacity 0.4s ease, transform 0.22s ease, box-shadow 0.22s ease, background 0.22s ease;
}

#btn-back:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 28px oklch(39% 0.05 53 / 0.22);
}

#btn-back.near-bottom {
  background: linear-gradient(180deg, oklch(99% 0.012 82), oklch(95% 0.03 84));
  color: var(--ink-warm);
  transform: translateY(-2px);
  box-shadow:
    0 12px 30px oklch(39% 0.05 53 / 0.24),
    0 0 0 2px oklch(80% 0.11 84 / 0.34);
}
