* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

body {
  font-family: "DM Sans", sans-serif;
  background: #281027;
  color: white;
}

button {
  font: inherit;
  border: 0;
  cursor: pointer;
}


/* ================================
   MAIN
================================ */

#app {
  height: 100dvh;
  width: 100%;
  position: relative;
}


/* ================================
   SCREENS
================================ */

.screen {
  position: absolute;
  inset: 0;

  display: none;

  align-items: center;
  justify-content: center;

  overflow: hidden;

  padding: 28px 20px;

  background: #2b102d;
}

.screen.active {
  display: flex;

  animation: screenIn 0.75s ease both;
}


/* stars */

.screen::after {
  content: "";

  position: absolute;
  inset: 0;

  pointer-events: none;

  background-image:

    radial-gradient(
      circle at 15% 20%,
      rgba(255,255,255,.55) 0 1px,
      transparent 2px
    ),

    radial-gradient(
      circle at 80% 28%,
      rgba(255,255,255,.35) 0 1px,
      transparent 2px
    ),

    radial-gradient(
      circle at 67% 78%,
      rgba(255,255,255,.25) 0 1px,
      transparent 2px
    );

  background-size:
    260px 240px,
    310px 300px,
    390px 330px;

  opacity: .55;
}


/* tap instruction */

.screen.active:not(.hero)::before {

  content: "tap anywhere to continue";

  position: absolute;

  bottom: 72px;

  left: 50%;

  transform: translateX(-50%);

  z-index: 3;

  font-size: 10px;

  letter-spacing: .12em;

  text-transform: uppercase;

  color: rgba(255,255,255,.38);

  pointer-events: none;
}


/* ================================
   COMMON
================================ */

.eyebrow {

  font-size: 14px;

  font-style: italic;

  letter-spacing: .08em;

  color: #f2a4ba;

  margin: 0 0 12px;
}


.sub {

  font-size: 14px;

  color: #e5cbd9;

  margin: 6px 0 24px;
}


/* ================================
   OPENING
================================ */

.hero {

  background:
    linear-gradient(
      160deg,
      #fff8f0,
      #ffdccc
    );
}

.hero::after {
  opacity: .12;
}


.hero .eyebrow {

  color: #b94d69;

  position: absolute;

  top: 16%;
}


.soft-orb {

  position: absolute;

  width: 520px;

  height: 520px;

  max-width: 130vw;

  max-height: 130vw;

  border-radius: 50%;

  background:
    radial-gradient(
      circle,
      rgba(255,110,150,.55) 0,
      rgba(255,150,165,.38) 28%,
      rgba(255,185,175,.16) 50%,
      transparent 70%
    );

  filter: blur(14px);

  transform: translateY(46px);

  transition: transform .9s ease, opacity .9s ease;

}


.heart {

  position: relative;

  font-size: 105px;

  color: #ec3270;

  text-shadow:
    0 18px 35px rgba(197,33,95,.3);

  animation: heartbeat 1.35s ease-in-out infinite;

  z-index: 1;

  cursor: pointer;
}


.hint {

  position: absolute;

  bottom: 11%;

  font-size: 12px;

  opacity: .65;

  letter-spacing: .08em;

  color: #6f3248;
}


/* ================================
   BIRTHDAY
================================ */

.birthday,
.letter-screen,
.reasons-screen,
.cake-screen,
.tree-screen {

  background:

    radial-gradient(
      circle at 50% 35%,
      #55213f 0,
      #2b102d 52%,
      #18091b 100%
    );
}


.birthday-card {

  text-align: center;

  position: relative;

  z-index: 1;
}


.birthday h1 {

  font-size:
    clamp(46px, 15vw, 86px);

  line-height: .92;

  margin: 0;

  font-weight: 700;

  letter-spacing: -.04em;

  color: #fff3e7;
}


.birthday .name {

  font-family:
    "Playfair Display",
    serif;

  font-size:
    clamp(54px, 17vw, 96px);

  font-style: italic;

  color: #f0a72c;

  margin-top: 8px;
}


.cat {

  font-size: 74px;

  margin-top: 18px;

  filter:
    drop-shadow(
      0 10px 18px rgba(0,0,0,.25)
    );
}


.tiny {

  color: #d9bacb;

  font-size: 13px;
}


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

.letter {

  width: min(440px, 92vw);

  padding: 30px 27px 32px;

  background:

    repeating-linear-gradient(
      #fff4dd 0 31px,
      #efd8b8 32px,
      #fff4dd 33px
    );

  color: #49333a;

  border:
    1px solid #f3b84d;

  border-radius: 20px;

  box-shadow:
    0 20px 60px rgba(0,0,0,.3);

  position: relative;

  z-index: 1;

  transform: rotate(-.5deg);
}


.letter h2 {

  font-family:
    "Playfair Display",
    serif;

  color: #9a5a18;

  margin: 0 0 19px;

  font-size: 23px;
}


.letter p {

  font-family:
    "Playfair Display",
    serif;

  font-size: 16px;

  line-height: 1.75;

  margin: 0 0 13px;
}


.letter .closing {

  color: #8e4d21;
}


.signature {

  text-align: right;

  color: #a86a1f;

  font-style: italic;

  line-height: 1.5;

  margin-top: 15px;
}


/* ================================
   CONTENT
================================ */

.content,
.cake-wrap,
.tree-wrap,
.final-card {

  position: relative;

  z-index: 2;

  text-align: center;

  width: min(620px, 94vw);
}


.content h2,
.cake-wrap h2,
.tree-wrap h2 {

  font-family:
    "Playfair Display",
    serif;

  font-size:
    clamp(34px, 9vw, 54px);

  margin: 0;

  color: #fff1e7;
}


/* ================================
   BALLOONS
================================ */

.balloons {

  display: flex;

  justify-content: center;

  align-items: flex-end;

  gap: 10px;

  flex-wrap: wrap;

  max-width: 480px;

  margin: 10px auto;
}


.balloon {

  width: 68px;

  height: 90px;

  border-radius:
    50% 50% 48% 48%;

  position: relative;

  transition: .25s;

  box-shadow:

    inset 10px 8px 18px
      rgba(255,255,255,.32),

    0 12px 24px
      rgba(0,0,0,.22);
}


.balloon::after {

  content: "";

  position: absolute;

  bottom: -36px;

  left: 50%;

  width: 1px;

  height: 38px;

  background: #cbaebd;
}


.balloon span {

  font-size: 25px;

  color: rgba(255,255,255,.8);
}


.balloon:hover {

  transform:
    translateY(-8px)
    rotate(-4deg);
}


.pink {

  background:
    linear-gradient(
      135deg,
      #ffb3ca,
      #e9518c
    );
}


.yellow {

  background:
    linear-gradient(
      135deg,
      #ffe18a,
      #eebd39
    );
}


.purple {

  background:
    linear-gradient(
      135deg,
      #cdb9ff,
      #8366dc
    );
}


.blue {

  background:
    linear-gradient(
      135deg,
      #8ff0e8,
      #27aaa9
    );
}


.peach {

  background:
    linear-gradient(
      135deg,
      #ffc5ae,
      #ef6d50
    );
}


.balloon.popped {

  animation:
    pop .35s forwards;
}


.reason-box {

  margin:
    28px auto 0;

  padding:
    14px 18px;

  border-radius: 16px;

  background:
    rgba(255,255,255,.08);

  border:
    1px solid
    rgba(255,184,218,.35);

  font-size: 15px;

  color: #fff4f7;

  max-width: 430px;
}


.hidden {
  display: none;
}


.count {

  font-size: 13px;

  color: #d4aabd;

  margin-top: 28px;
}


/* ================================
   CAKE  (build → light → blow out)
================================ */

.cake {

  --b: .45s;               /* build starts after the screen fades in */

  position: relative;

  width: 230px;

  height: 210px;

  background: transparent;

  margin:
    10px auto 0;

  display: block;

  padding: 0;

  border: 0;

  outline: none;

  cursor: pointer;

  user-select: none;

  touch-action: manipulation;

  -webkit-tap-highlight-color: transparent;
}


/* everything is hidden until the build animation brings it in */

.cake .plate,
.cake .tier,
.cake .icing,
.cake .drip,
.cake .dollop,
.cake .sprinkle,
.cake .cherry,
.cake .glob,
.cake .candle {
  opacity: 0;
}


/* ---------- warm light that fills in when candles are lit ---------- */

.cake-glow {

  position: absolute;

  z-index: -1;

  left: -70px;

  top: -100px;

  width: 370px;

  height: 370px;

  border-radius: 50%;

  background:
    radial-gradient(
      circle,
      rgba(255,176,90,.5) 0,
      rgba(255,120,90,.2) 34%,
      rgba(255,90,120,0) 68%
    );

  opacity: 0;

  transition: opacity .9s ease;

  pointer-events: none;
}

.cake.is-lit .cake-glow {

  opacity: 1;

  animation:
    glowPulse 1.3s ease-in-out infinite alternate;
}

.cake.is-lit .tier,
.cake.is-lit .icing {
  filter: brightness(1.15) saturate(1.05);
}

.tier,
.icing {
  transition: filter .8s ease;
}


/* ---------- plate ---------- */

.plate {

  position: absolute;

  z-index: 0;

  left: -8px;

  top: 176px;

  width: 246px;

  height: 28px;

  border-radius: 50%;

  background:
    radial-gradient(
      ellipse at 50% 35%,
      #fff4f7 0,
      #f3cfdb 62%,
      #d99fb4 100%
    );

  box-shadow:
    0 10px 18px rgba(0,0,0,.35),
    inset 0 -3px 5px rgba(160,80,110,.25);
}


/* ---------- tiers ---------- */

.tier {

  position: absolute;

  z-index: 1;

  left: 15px;

  width: 200px;

  border-radius:
    15px 15px 10px 10px;

  background:
    linear-gradient(
      #9e554e,
      #6e383d
    );

  box-shadow:
    0 12px 20px
      rgba(0,0,0,.25);

  transform-origin: 50% 100%;
}

.tier::before {

  content: "";

  position: absolute;

  left: 0;

  right: 0;

  border-radius: 3px;

  background:
    linear-gradient(
      #ffe3e9,
      #ffc9d5
    );
}


.tier.bottom {

  height: 62px;

  bottom: 25px;
}

.tier.bottom::before {
  top: 24px;
  height: 7px;
}

/* row of little pearls around the base */
.tier.bottom::after {

  content: "";

  position: absolute;

  left: 6px;

  right: 6px;

  bottom: 7px;

  height: 8px;

  background:
    radial-gradient(
      circle at 50% 50%,
      #ffe9ef 0 3.4px,
      transparent 3.9px
    )
    0 0 / 14px 8px repeat-x;
}


.tier.top {

  height: 55px;

  bottom: 80px;

  width: 165px;

  left: 32px;

  background:
    linear-gradient(
      #b36a58,
      #82423e
    );
}

.tier.top::before {
  top: 30px;
  height: 6px;
}


/* ---------- cream ---------- */

.icing {

  position: absolute;

  z-index: 2;

  bottom: 116px;

  left: 29px;

  width: 171px;

  height: 22px;

  background: #ffd8db;

  border-radius: 15px;

  box-shadow:
    inset 0 -4px 0 rgba(255,150,178,.4),
    0 2px 4px rgba(0,0,0,.15);
}

/* the big blob of cream that lands and spreads into the icing */
.glob {

  position: absolute;

  z-index: 3;

  left: 101px;

  top: 62px;

  width: 26px;

  height: 26px;

  border-radius: 50%;

  transform-origin: 50% 100%;

  background:
    radial-gradient(
      circle at 35% 30%,
      #fff 0,
      #ffe6ec 45%,
      #ffc0d1 100%
    );
}

/* cream running down the side */
.drip {

  position: absolute;

  z-index: 3;

  top: 86px;

  width: 9px;

  height: 0;

  background: #ffd8db;

  border-radius: 0 0 5px 5px;
}

.drip::after {

  content: "";

  position: absolute;

  left: -2px;

  bottom: -4px;

  width: 13px;

  height: 13px;

  border-radius: 50%;

  background: #ffd8db;
}

/* piped cream rosettes */
.dollop {

  position: absolute;

  z-index: 3;

  top: 60px;

  left: calc(var(--x) - 11px);

  width: 22px;

  height: 20px;

  border-radius: 50% 50% 44% 44%;

  transform-origin: 50% 100%;

  background:
    radial-gradient(
      circle at 35% 30%,
      #fff 0,
      #ffe6ec 40%,
      #ffc0d1 100%
    );

  box-shadow:
    inset 0 -3px 4px rgba(236,100,140,.28);
}

.dollop::before {

  content: "";

  position: absolute;

  left: 5px;

  top: -6px;

  width: 12px;

  height: 10px;

  border-radius: 50% 50% 40% 40%;

  background: inherit;
}

.sprinkle {

  position: absolute;

  z-index: 3;

  width: 8px;

  height: 3px;

  border-radius: 2px;

  transform: rotate(var(--r));
}

.cherry {

  position: absolute;

  z-index: 3;

  top: 44px;

  left: calc(var(--x) - 8px);

  width: 16px;

  height: 16px;

  border-radius: 50%;

  transform-origin: 50% 100%;

  background:
    radial-gradient(
      circle at 32% 30%,
      #ff9aa9 0,
      #e8203f 40%,
      #a50d2a 100%
    );

  box-shadow:
    0 2px 3px rgba(0,0,0,.3);
}

.cherry::before {

  content: "";

  position: absolute;

  left: 8px;

  top: -10px;

  width: 2px;

  height: 12px;

  border-radius: 2px;

  background: #5f8f3d;

  transform-origin: 50% 100%;

  transform: rotate(22deg);
}

.cherry::after {

  content: "";

  position: absolute;

  left: 3px;

  top: 3px;

  width: 4px;

  height: 3px;

  border-radius: 50%;

  background: rgba(255,255,255,.7);

  transform: rotate(-30deg);
}


/* ---------- candles + flames ---------- */

.candle {

  position: absolute;

  z-index: 4;

  bottom: 137px;

  width: 12px;

  height: 48px;

  background:
    repeating-linear-gradient(
      -45deg,
      #fff1c9 0 6px,
      #ffd0dc 6px 12px
    );

  border-radius: 5px;

  transform-origin: 50% 100%;
}

.candle::before {

  content: "";

  position: absolute;

  left: 5px;

  top: -6px;

  width: 2px;

  height: 7px;

  border-radius: 1px;

  background: #3b2b2b;
}

.candle.smoking::before {
  animation: ember 1.8s ease-out forwards;
}


.c1 {
  left: 74px;
  --i: 0;
}

.c2 {
  left: 109px;
  --i: 1;
}

.c3 {
  left: 144px;
  --i: 2;
}


.flame {

  position: absolute;

  z-index: 5;

  bottom: 181px;

  width: 15px;

  height: 26px;

  opacity: 0;

  transform: scale(0);

  transform-origin: 50% 100%;

  pointer-events: none;

  filter:
    drop-shadow(0 0 6px #ff9d32)
    drop-shadow(0 0 14px rgba(255,150,50,.6));
}

.flame::before {

  content: "";

  position: absolute;

  inset: 0;

  background:
    radial-gradient(
      ellipse at 50% 78%,
      #fff3b8 0,
      #ffd24d 34%,
      #ff9a2e 70%,
      #ff5f1f 100%
    );

  clip-path:
    path("M7.5 0 C9.6 7 15 11 15 18.5 A7.5 7.5 0 0 1 0 18.5 C0 11 5.4 7 7.5 0 Z");
}

.flame::after {

  content: "";

  position: absolute;

  left: 4.5px;

  bottom: 1.5px;

  width: 6px;

  height: 12px;

  opacity: .9;

  background:
    linear-gradient(
      #fff 0,
      #d6ecff 50%,
      #8cc4ff 100%
    );

  clip-path:
    path("M3 0 C4.4 3.6 6 5.4 6 8.4 A3 3.6 0 0 1 0 8.4 C0 5.4 1.6 3.6 3 0 Z");
}


.f1 {
  left: 73px;
}

.f2 {
  left: 108px;
}

.f3 {
  left: 143px;
}

.flame.lit {

  animation:
    flameIgnite .55s cubic-bezier(.2,1.2,.4,1) both,
    flameDance 1.05s ease-in-out .55s infinite alternate;
}

.f2.lit {
  animation-delay: 0s, .75s;
}

.f3.lit {
  animation-delay: 0s, .95s;
}

.flame.out {

  animation:
    flameBlow .6s ease-in both;

  animation-delay: var(--bd, 0s);
}


/* ---------- effect particles (created by script.js) ---------- */

.fx {

  position: absolute;

  left: 0;

  top: 0;

  pointer-events: none;

  z-index: 7;
}

.puff {

  width: 16px;

  height: 16px;

  margin: -8px 0 0 -8px;

  border-radius: 50%;

  background:
    radial-gradient(
      circle,
      rgba(255,236,242,.85),
      rgba(255,236,242,0) 70%
    );
}

.smoke {

  width: 14px;

  height: 14px;

  margin: -7px 0 0 -7px;

  border-radius: 50%;

  filter: blur(1.5px);

  background:
    radial-gradient(
      circle,
      rgba(255,255,255,.55),
      rgba(255,255,255,0) 70%
    );
}

.spark {

  width: 4px;

  height: 4px;

  margin: -2px 0 0 -2px;

  border-radius: 50%;

  background: #ffe08a;

  box-shadow:
    0 0 6px 1px #ffb347;
}

.orb {

  width: 12px;

  height: 12px;

  margin: -6px 0 0 -6px;

  border-radius: 50%;

  background:
    radial-gradient(
      circle,
      #fff 0,
      #ffe08a 40%,
      #ff9a2e 70%,
      rgba(255,154,46,0) 100%
    );

  box-shadow:
    0 0 14px 5px rgba(255,170,70,.7);
}

.wind {

  width: 70px;

  height: 2px;

  border-radius: 2px;

  background:
    linear-gradient(
      90deg,
      rgba(255,255,255,0),
      rgba(255,255,255,.8)
    );
}


/* full-screen layer for confetti, hearts and the wish star */

.cake-fx {

  position: absolute;

  inset: 0;

  z-index: 6;

  overflow: hidden;

  pointer-events: none;
}

.cake-fx .flash {

  position: absolute;

  inset: 0;
}

.cake-fx .conf {

  position: absolute;

  left: 0;

  top: 0;

  will-change: transform, opacity;
}

.cake-fx .rise-heart {

  position: absolute;

  left: 0;

  top: 0;

  line-height: 1;

  color: #ff8fb8;

  text-shadow:
    0 0 8px rgba(255,80,145,.55);
}

.cake-fx .wish-star {

  position: absolute;

  left: 0;

  top: 0;

  margin: -14px 0 0 -11px;

  font-size: 24px;

  line-height: 1;

  color: #fff6c8;

  text-shadow:
    0 0 10px #ffd86b,
    0 0 22px #ff9acb;
}

.cake-fx .trail {

  position: absolute;

  left: 0;

  top: 0;

  width: 6px;

  height: 6px;

  margin: -3px 0 0 -3px;

  border-radius: 50%;

  background: #ffe9a8;

  box-shadow:
    0 0 8px 2px rgba(255,190,120,.8);
}

.cake-fx .spark {
  z-index: 1;
}


/* ---------- text under the cake ---------- */

.cake-message {

  font-family:
    "Playfair Display",
    serif;

  font-style: italic;

  color: #f1c8d5;

  font-size: 16px;

  min-height: 1.4em;

  opacity: 0;

  transform: translateY(8px);

  transition:
    opacity .7s ease,
    transform .7s ease;
}

.cake-message.show {

  opacity: 1;

  transform: none;
}

.sub-swap {
  animation: subSwap .5s ease both;
}

/* "tap anywhere to continue" only once the candles have been blown out */
.screen.cake-screen::before {
  transition: opacity .6s ease;
}

.screen.cake-screen:not(.is-done)::before {
  opacity: 0;
}


/* ---------- cake animation keyframes ---------- */

.cake.is-ready {
  animation: cakeBob 2.4s ease-in-out infinite;
}

.cake.build .plate {
  animation:
    plateIn .55s cubic-bezier(.2,.9,.3,1.2) var(--b) both;
}

.cake.build .tier.bottom {
  animation:
    tierDrop .95s linear calc(var(--b) + .25s) both,
    tierJolt .5s ease-out calc(var(--b) + 1.58s) forwards;
}

.cake.build .tier.top {
  animation:
    tierDrop .95s linear calc(var(--b) + 1s) both;
}

.cake.build .glob {
  animation:
    globDrop .5s linear calc(var(--b) + 1.5s) both;
}

.cake.build .icing {
  animation:
    icingSpread .6s cubic-bezier(.2,.9,.3,1.1) calc(var(--b) + 1.9s) both;
}

.cake.build .drip {
  animation:
    dripRun 1s cubic-bezier(.3,.6,.4,1)
    calc(var(--b) + 2.3s + var(--i) * .08s) both;
}

.cake.build .dollop {
  animation:
    dollopDrop .7s linear
    calc(var(--b) + 2.2s + var(--i) * .14s) both;
}

.cake.build .sprinkle {
  animation:
    sprinkleDrop .6s cubic-bezier(.45,0,.9,.55)
    calc(var(--b) + 2.4s + var(--d)) both;
}

.cake.build .cherry {
  animation:
    cherryDrop .7s linear
    calc(var(--b) + 2.95s + var(--i) * .15s) both;
}

.cake.build .candle {
  animation:
    candleDrop .6s linear
    calc(var(--b) + 3s + var(--i) * .14s) both;
}


@keyframes plateIn {
  from { opacity: 0; transform: scale(.4,.2); }
  to   { opacity: 1; transform: none; }
}

@keyframes tierDrop {
  0%   { opacity: 0; transform: translateY(-190px) scale(.96,1.06);
         animation-timing-function: cubic-bezier(.5,0,.9,.6); }
  28%  { opacity: 0; }
  36%  { opacity: 1; }
  58%  { opacity: 1; transform: translateY(0) scale(.98,1.05);
         animation-timing-function: ease-out; }
  70%  { transform: translateY(0) scale(1.08,.84);
         animation-timing-function: ease-in-out; }
  84%  { transform: translateY(-3px) scale(.98,1.04); }
  100% { opacity: 1; transform: none; }
}

@keyframes tierJolt {
  0%   { transform: none; }
  30%  { transform: translateY(2px) scale(1.02,.97); }
  60%  { transform: translateY(-1px); }
  100% { transform: none; }
}

@keyframes globDrop {
  0%   { opacity: 0; transform: translateY(-200px) scale(.7,1.4);
         animation-timing-function: cubic-bezier(.5,0,.9,.55); }
  24%  { opacity: 0; }
  32%  { opacity: 1; }
  80%  { opacity: 1; transform: translateY(0) scale(.85,1.3); }
  92%  { opacity: 1; transform: translateY(0) scale(1.9,.5); }
  100% { opacity: 0; transform: translateY(0) scale(2.4,.3); }
}

@keyframes icingSpread {
  0%   { opacity: 0; transform: scale(.1,.3); }
  30%  { opacity: 1; }
  70%  { transform: scale(1.05,1.15); }
  100% { opacity: 1; transform: none; }
}

@keyframes dripRun {
  0%   { height: 0; opacity: 0; }
  6%   { opacity: 1; }
  60%  { height: calc(var(--h) * 1.18); }
  100% { height: var(--h); opacity: 1; }
}

@keyframes dollopDrop {
  0%   { opacity: 0; transform: translateY(-190px) scale(.75,1.35);
         animation-timing-function: cubic-bezier(.5,0,.9,.55); }
  24%  { opacity: 0; }
  32%  { opacity: 1; }
  62%  { opacity: 1; transform: translateY(0) scale(.85,1.3);
         animation-timing-function: ease-out; }
  76%  { transform: translateY(0) scale(1.3,.7); }
  90%  { transform: translateY(-2px) scale(.95,1.07); }
  100% { opacity: 1; transform: none; }
}

@keyframes sprinkleDrop {
  0%   { opacity: 0; transform: translateY(-170px) rotate(calc(var(--r) + 260deg)); }
  24%  { opacity: 0; }
  32%  { opacity: 1; }
  72%  { opacity: 1; transform: translateY(0) rotate(var(--r)); }
  86%  { transform: translateY(-4px) rotate(var(--r)); }
  100% { opacity: 1; transform: translateY(0) rotate(var(--r)); }
}

@keyframes cherryDrop {
  0%   { opacity: 0; transform: translateY(-190px);
         animation-timing-function: cubic-bezier(.5,0,.9,.55); }
  24%  { opacity: 0; }
  32%  { opacity: 1; }
  58%  { opacity: 1; transform: translateY(0);
         animation-timing-function: ease-out; }
  70%  { transform: translateY(-14px);
         animation-timing-function: ease-in; }
  80%  { transform: translateY(0) scale(1.12,.88); }
  90%  { transform: translateY(-4px); }
  100% { opacity: 1; transform: none; }
}

@keyframes candleDrop {
  0%   { opacity: 0; transform: translateY(-170px) rotate(7deg);
         animation-timing-function: cubic-bezier(.5,0,.9,.55); }
  24%  { opacity: 0; }
  32%  { opacity: 1; }
  62%  { opacity: 1; transform: translateY(0) rotate(0);
         animation-timing-function: ease-out; }
  74%  { transform: translateY(3px) scaleY(.94); }
  88%  { transform: translateY(-2px); }
  100% { opacity: 1; transform: none; }
}

@keyframes flameIgnite {
  0%   { opacity: 0; transform: scale(0); }
  35%  { opacity: 1; transform: scale(1.55,1.8); }
  65%  { transform: scale(.88,.9); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes flameDance {
  0%   { opacity: 1; transform: scale(1,1) rotate(-2deg); }
  50%  { opacity: 1; transform: scale(1.06,1.14) rotate(2.5deg); }
  100% { opacity: 1; transform: scale(.95,1.03) rotate(-1deg); }
}

@keyframes flameBlow {
  0%   { opacity: 1; transform: scale(1) rotate(0); }
  30%  { opacity: 1; transform: scale(1.15,.8) rotate(48deg) translateX(2px); }
  65%  { opacity: .8; transform: scale(.8,.45) rotate(78deg) translateX(5px); }
  100% { opacity: 0; transform: scale(.2,.1) rotate(88deg) translateX(8px); }
}

@keyframes ember {
  0%   { background: #ffb347; box-shadow: 0 0 6px 2px #ff8a1f; }
  100% { background: #3b2b2b; box-shadow: 0 0 0 0 transparent; }
}

@keyframes glowPulse {
  from { transform: scale(.95); }
  to   { transform: scale(1.06); }
}

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

@keyframes subSwap {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}


/* ================================
   TREE — centered, branch-aligned
================================ */

.tree {
  position: relative;
  width: 760px;
  height: 500px;

  /* The tree is wider than .tree-wrap, so explicitly center
     the tree itself instead of relying on auto margins. */
  left: 50%;
  transform: translateX(-50%);

  margin: 0 0 -8px;
  overflow: visible;
}

.tree-branches {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  z-index: 1;
  pointer-events: none;
}

.branch-lines {
  fill: none;
  stroke: #633445;
  stroke-width: 8;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .72;
  transform-origin: 50% 100%;
  animation: treeSway 8s ease-in-out infinite;
}

.tree-heart-field {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
}

.tree-heart {
  position: absolute;
  line-height: 1;
  display: block;
  font-family: Arial, sans-serif;
  font-style: normal;
  transform-origin: center;
  will-change: transform, opacity, filter;

  /* Bright, unmistakable glowing hearts */
  text-shadow:
    0 0 3px #fff,
    0 0 6px rgba(255, 190, 220, 1),
    0 0 12px rgba(255, 95, 160, .98),
    0 0 22px rgba(255, 70, 145, .8),
    0 0 36px rgba(255, 70, 145, .48);

  animation:
    heartTwinkle var(--twinkle) ease-in-out infinite,
    heartFloat var(--float) ease-in-out infinite;

  animation-delay: var(--delay);
}

.tree-text {
  color: #efd3df;
  font-size: 15px;
  line-height: 1.8;
}

@keyframes treeSway {
  0%, 100% { transform: rotate(-.35deg); }
  50% { transform: rotate(.35deg); }
}

@keyframes heartTwinkle {
  0%, 100% {
    opacity: .62;
    filter: brightness(.95)
            drop-shadow(0 0 3px rgba(255,100,165,.45));
    text-shadow:
      0 0 3px #fff,
      0 0 6px rgba(255,190,220,.75),
      0 0 13px rgba(255,95,160,.7);
  }

  22% {
    opacity: .88;
    filter: brightness(1.15)
            drop-shadow(0 0 8px rgba(255,110,170,.8));
  }

  42% {
    opacity: 1;
    filter: brightness(1.65)
            drop-shadow(0 0 18px rgba(255,110,175,1))
            drop-shadow(0 0 32px rgba(255,90,160,.7));
    text-shadow:
      0 0 3px #fff,
      0 0 8px #fff,
      0 0 15px rgba(255,190,220,1),
      0 0 28px rgba(255,85,155,1),
      0 0 42px rgba(255,65,145,.75);
  }

  58% {
    opacity: .78;
    filter: brightness(1.08)
            drop-shadow(0 0 7px rgba(255,105,165,.7));
  }

  76% {
    opacity: 1;
    filter: brightness(1.45)
            drop-shadow(0 0 14px rgba(255,110,170,.95));
  }
}

@keyframes heartFloat {
  0%, 100% {
    transform: translateY(0) scale(1) rotate(-2deg);
  }
  50% {
    transform: translateY(-4px) scale(1.055) rotate(2deg);
  }
}

@media (max-width: 430px) {
  .tree {
    width: 760px;
    max-width: none;
    transform: scale(.68);
    transform-origin: center center;
    margin: -90px auto -95px;
  }
}

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

.final-screen {

  background:
    linear-gradient(
      135deg,
      #fff4e8,
      #ffd2cf 48%,
      #fff0d2
    );
}


.final-screen::after {
  opacity: .15;
}


.final-card {

  color: #532a3d;
}


.final-card .eyebrow {

  color: #a45169;
}


.final-card h1 {

  font-size:
    clamp(43px, 13vw, 78px);

  line-height: .95;

  margin: 10px 0 18px;

  letter-spacing: -.04em;
}


.final-card h1 em {

  font-family:
    "Playfair Display",
    serif;

  color: #d32769;
}


.final-card > p {

  font-size: 15px;
}


.divider {

  margin: 25px 0;

  color: #e8a63d;
}


.big-line {

  font-family:
    "Playfair Display",
    serif;

  font-size: 20px !important;

  line-height: 1.5;
}


.final-card button {

  margin-top: 22px;

  background: #56253e;

  color: #fff0f4;

  padding: 13px 20px;

  border-radius: 999px;
}


/* ================================
   MUSIC
================================ */

.music-pill {

  position: fixed;

  right: 14px;

  top: 14px;

  z-index: 30;

  display: flex;

  align-items: center;

  gap: 8px;

  background:
    rgba(38,13,35,.66);

  backdrop-filter: blur(10px);

  border:
    1px solid
    rgba(255,255,255,.15);

  padding:
    6px 10px 6px 6px;

  border-radius: 999px;

  color: white;
}


.music-pill button {

  width: 31px;

  height: 31px;

  border-radius: 50%;

  background: #f0a13c;

  color: #351329;

  font-weight: 700;
}


.music-pill strong {

  display: block;

  font-size: 12px;
}


.music-pill span {

  display: block;

  font-size: 9px;

  opacity: .65;
}


.music-pill.playing button {

  animation:
    spin 2s linear infinite;
}


/* ================================
   FLOATING HEARTS
================================ */

.floating-hearts {

  position: absolute;

  inset: 0;

  pointer-events: none;

  background-image:

    radial-gradient(
      circle,
      #ef5c8e 0 5px,
      transparent 6px
    ),

    radial-gradient(
      circle,
      #ffb24a 0 4px,
      transparent 5px
    );

  background-size:
    150px 190px,
    210px 230px;

  opacity: .22;
}


/* ================================
   ANIMATIONS
================================ */

@keyframes screenIn {

  from {

    opacity: 0;

    transform:
      scale(1.015);

  }

  to {

    opacity: 1;

    transform:
      scale(1);

  }
}


@keyframes heartbeat {

  50% {

    transform:
      scale(1.08);

  }
}


@keyframes pop {

  to {

    transform:
      scale(1.5);

    opacity: 0;

  }
}


@keyframes flicker {

  to {

    transform:
      scale(1.12)
      translateY(-3px);

  }
}


@keyframes float {

  50% {

    transform:
      translateY(-8px);

  }
}


@keyframes spin {

  to {

    transform:
      rotate(360deg);

  }
}


/* ================================
   MOBILE
================================ */

@media (max-width: 430px) {

  .letter {

    padding:
      25px 21px;
  }


  .letter p {

    font-size: 14px;

    line-height: 1.7;
  }


  .balloon {

    width: 58px;

    height: 78px;
  }


  .tree {

    transform:
      translateX(-50%)
      scale(.52);

    margin:
      -35px auto -45px;
  }


  .music-pill {

    right: 9px;

    top: 9px;
  }


  .music-pill span {

    display: none;
  }


  .music-pill {

    padding-right: 6px;
  }

}


/* ================================
   DESKTOP
================================ */

/* Tree screen: keep the tap instruction below the closing quote */
@media (max-width: 430px) {
  .tree-screen.active::before {
    bottom: 2px;
  }

  .tree-screen .tree-text {
    margin-bottom: 100px;
  }
}


@media (min-width: 700px) {

  .screen {

    padding: 40px;
  }


  .letter {

    width: 520px;
  }


  .balloons {

    gap: 18px;
  }


  .balloon {

    width: 76px;

    height: 98px;
  }

}

/* =========================================
   BALLOONS — FLOATING ANIMATION
   Only animation added; existing styling/layout unchanged.
   ========================================= */

.balloon {
  animation: balloonFloat 3.6s ease-in-out infinite;
  will-change: transform;
}

.balloon:nth-child(1) {
  animation-delay: 0s;
}

.balloon:nth-child(2) {
  animation-delay: .55s;
}

.balloon:nth-child(3) {
  animation-delay: 1.1s;
}

.balloon:nth-child(4) {
  animation-delay: .35s;
}

.balloon:nth-child(5) {
  animation-delay: .9s;
}

@keyframes balloonFloat {
  0%, 100% {
    transform: translateY(0) rotate(-1deg);
  }

  50% {
    transform: translateY(-12px) rotate(1.5deg);
  }
}

/* Keep the pop animation exactly as before when a balloon is clicked. */
.balloon.popped {
  animation: pop .35s forwards;
}

/* =========================================
   REASONS SCREEN — TWINKLING STARS
   Uses the existing stars; no new elements/layout changes.
   ========================================= */

.reasons-screen::after {
  animation: starsShine 2.8s ease-in-out infinite;
}

@keyframes starsShine {
  0%, 100% {
    opacity: .42;
    filter: brightness(.85);
  }

  35% {
    opacity: .72;
    filter: brightness(1.35);
  }

  50% {
    opacity: .9;
    filter: brightness(1.8);
  }

  70% {
    opacity: .58;
    filter: brightness(1.1);
  }
}

/* Slightly offset the existing star pattern timing so the shine feels organic. */
.reasons-screen::after {
  animation-delay: -.8s;
}


/* =========================================
   OPENING — BOW & ARROW HEART SHOT
   Bow + arrow are positioned and animated from
   script.js so the arrow always flies at the heart.
   ========================================= */

.archery-scene {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.bow,
.arrow {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.bow svg,
.arrow svg {
  position: absolute;
  left: 0;
  top: 0;
  overflow: visible;
}

.bow svg {
  filter: drop-shadow(0 5px 5px rgba(80, 40, 20, .16));
}

.bow-limb {
  fill: none;
  stroke: #6b3b1d;
  stroke-width: 5.5;
  stroke-linecap: round;
}

.bow-grip {
  fill: #4e2815;
}

.bow-string {
  fill: none;
  stroke: rgba(83, 51, 31, .62);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.arrow {
  z-index: 5;
}

.arrow.gone {
  opacity: 0;
  transition: opacity .3s ease;
}

/* Dotted aim guide that marches toward the heart. */
.aim-line {
  position: absolute;
  left: 0;
  top: -1.5px;
  height: 3px;
  width: 0;
  opacity: 0;
  transform-origin: 0 50%;
  background:
    radial-gradient(circle, rgba(236, 50, 112, .75) 0 1.6px, transparent 2.2px)
    0 50% / 12px 3px repeat-x;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 25%, #000 75%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 25%, #000 75%, transparent);
  animation: aimMarch .6s linear infinite;
}

@keyframes aimMarch {
  to { background-position-x: 12px; }
}

/* While aiming: heart gets nervous, glow swells, hint fades. */
.hero .hint {
  transition: opacity .4s ease;
}

.hero.aiming .hint {
  opacity: 0;
}

.hero.aiming .heart {
  animation-duration: .6s;
}

.hero.aiming .soft-orb {
  transform: translateY(46px) scale(1.14);
}

/* Glossy SVG heart. */
.heart-core {
  display: block;
  position: relative;
  z-index: 2;
  width: 108px;
  height: 100px;
  transform-origin: center;
}

.heart-core svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(0 14px 18px rgba(197, 33, 95, .32));
}

/* Heart hit. */
.heart.hit .heart-core {
  animation: heartImpact .38s ease-out forwards;
}

@keyframes heartImpact {
  0% { transform: scale(1); }
  25% { transform: scale(1.18) rotate(-4deg); }
  50% { transform: scale(.88) rotate(4deg); }
  72% { transform: scale(1.07) rotate(-2deg); }
  100% { transform: scale(1); }
}

.heart.shattered .heart-core {
  animation: heartBurst .58s ease-out forwards;
}

@keyframes heartBurst {
  0% {
    opacity: 1;
    transform: scale(1);
    filter: brightness(1);
  }
  35% {
    opacity: 1;
    transform: scale(1.28);
    filter: brightness(1.65);
  }
  100% {
    opacity: 0;
    transform: scale(1.85);
    filter: brightness(2);
  }
}

/* Soft impact ring. */
.heart-burst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 28px;
  height: 28px;
  border: 2px solid rgba(255, 104, 158, .8);
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(.2);
  opacity: 0;
  pointer-events: none;
}

.heart.hit .heart-burst {
  animation: impactRing .62s ease-out forwards;
}

@keyframes impactRing {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(.2);
  }
  20% { opacity: .9; }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(4.5);
  }
}

/* Impact effects are created in script.js at the heart's centre. */
.impact-particles {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  z-index: 8;
  pointer-events: none;
}

.fx-particle,
.fx-ring,
.fx-flash {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
}

.fx-particle {
  font-style: normal;
  line-height: 1;
  will-change: transform, opacity;
  filter: drop-shadow(0 0 6px rgba(255, 80, 145, .55));
}

.fx-ring {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 3px solid #ffb04a;
}

.fx-flash {
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background:
    radial-gradient(
      circle,
      rgba(255, 255, 255, .95) 0,
      rgba(255, 150, 190, .55) 40%,
      transparent 70%
    );
}


/* =========================================
   LETTER — ENVELOPE OPENING
   Envelope drops in, flap opens, letter slides
   out and unfolds. Driven by classes from script.js:
   is-opening -> flap-behind -> is-out -> is-final -> is-open
   ========================================= */

.letter-screen {
  --ew: min(320px, 82vw);
}

.env-stage {
  position: relative;
  z-index: 2;
  flex: none;
  width: var(--ew);
  aspect-ratio: 3 / 2;
  perspective: 1100px;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}

.letter-screen.active .env-stage {
  animation: envIn .9s cubic-bezier(.2, .9, .25, 1) both;
}

@keyframes envIn {
  from {
    opacity: 0;
    transform: translateY(70px) scale(.86) rotate(-4deg);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.letter-screen.is-open .env-stage {
  cursor: default;
}

/* soft shadow under the envelope */
.env-stage::before {
  content: "";
  position: absolute;
  inset: 8% 3% -4% 3%;
  border-radius: 10px;
  box-shadow: 0 26px 46px rgba(0, 0, 0, .5);
  transition: opacity .6s ease;
  z-index: 0;
}

.letter-screen.is-final .env-stage::before {
  opacity: 0;
}

.env-part,
.flap-face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.env-back {
  z-index: 1;
  clip-path: inset(0 round 8px);
}

.env-front {
  z-index: 4;
  clip-path: inset(0 round 8px);
}

/* flap (hinged on its top edge) */
.env-flap {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 56%;
  z-index: 5;
  transform-origin: 50% 0;
  transform-style: preserve-3d;
  transition:
    transform .7s cubic-bezier(.4, .1, .3, 1) .25s,
    translate .95s cubic-bezier(.5, 0, .8, .4),
    opacity .8s ease;
}

.flap-face {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.flap-in {
  transform: rotateX(180deg);
}

.letter-screen.is-opening .env-flap {
  transform: rotateX(-180deg);
}

/* once open, the flap sits behind the letter */
.letter-screen.flap-behind .env-flap {
  z-index: 2;
}

/* wax seal */
.env-seal {
  position: absolute;
  left: 50%;
  top: 100%;
  translate: -50% -50%;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 22px;
  line-height: 1;
  color: #7a1f3d;
  background:
    radial-gradient(circle at 35% 30%, #ffd77a, #f0a72c 55%, #b8731a);
  box-shadow:
    0 4px 10px rgba(0, 0, 0, .35),
    inset 0 -3px 5px rgba(120, 60, 0, .35),
    inset 0 2px 3px rgba(255, 255, 255, .45);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  animation: sealGlow 1.8s ease-in-out infinite;
}

@keyframes sealGlow {
  0%, 100% {
    box-shadow:
      0 4px 10px rgba(0, 0, 0, .35),
      inset 0 -3px 5px rgba(120, 60, 0, .35),
      inset 0 2px 3px rgba(255, 255, 255, .45),
      0 0 0 0 rgba(255, 192, 68, .55);
  }
  50% {
    box-shadow:
      0 4px 10px rgba(0, 0, 0, .35),
      inset 0 -3px 5px rgba(120, 60, 0, .35),
      inset 0 2px 3px rgba(255, 255, 255, .45),
      0 0 0 13px rgba(255, 192, 68, 0);
  }
}

.letter-screen.is-opening .env-seal {
  animation: sealPop .38s ease-out forwards;
}

@keyframes sealPop {
  0% { transform: scale(1); opacity: 1; }
  40% { transform: scale(1.3) rotate(-8deg); opacity: 1; }
  100% { transform: scale(0); opacity: 0; }
}

/* envelope falls away once the letter is out */
.env-back,
.env-front {
  transition:
    translate .95s cubic-bezier(.5, 0, .8, .4),
    opacity .8s ease;
}

.letter-screen.is-final .env-back,
.letter-screen.is-final .env-front,
.letter-screen.is-final .env-flap {
  translate: 0 190px;
  opacity: 0;
}

/* THE LETTER: starts tucked inside, scaled down + clipped,
   slides out, then unfolds to full size in the centre. */
.letter-screen .letter {
  position: absolute;
  left: 50%;
  top: 0;
  z-index: 3;
  margin: 0;
  transform-origin: 50% 0;
  transform:
    translate(-50%, var(--ty0, 16%))
    scale(var(--s0, .6))
    rotate(-.5deg);
  clip-path: inset(-90px -90px var(--cut, 70%) -90px);
  transition:
    transform .95s cubic-bezier(.34, .1, .2, 1),
    clip-path .95s ease;
  will-change: transform;
}

.letter-screen.is-out .letter {
  transform:
    translate(-50%, var(--ty1, -40%))
    scale(var(--s0, .6))
    rotate(-.5deg);
}

.letter-screen.is-final .letter {
  z-index: 7;
  transform:
    translate(-50%, var(--ty2, 0px))
    scale(var(--sf, 1))
    rotate(-.5deg);
  clip-path: inset(-90px -90px -90px -90px);
  transition:
    transform 1.15s cubic-bezier(.22, .9, .25, 1),
    clip-path 1s ease .1s;
}

/* warm light that blooms behind the letter */
.letter-glow {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 620px;
  height: 620px;
  max-width: 150vw;
  max-height: 150vw;
  border-radius: 50%;
  background:
    radial-gradient(
      circle,
      rgba(255, 200, 130, .38) 0,
      rgba(255, 120, 160, .16) 40%,
      transparent 68%
    );
  transform: translate(-50%, -50%) scale(.5);
  opacity: 0;
  transition: opacity 1.4s ease, transform 1.6s ease;
  pointer-events: none;
  z-index: 1;
}

.letter-screen.is-opening .letter-glow {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* sparkles when the letter slides out */
.letter-fx {
  position: absolute;
  left: 50%;
  top: 0;
  width: 0;
  height: 0;
  z-index: 8;
  pointer-events: none;
}

/* "tap to open" hint under the envelope */
.env-hint {
  position: absolute;
  left: 50%;
  top: calc(50% + var(--ew) / 3 + 46px);
  transform: translateX(-50%);
  margin: 0;
  z-index: 2;
  font-size: 13px;
  font-style: italic;
  letter-spacing: .08em;
  color: #f2a4ba;
  opacity: .8;
  white-space: nowrap;
  pointer-events: none;
  animation: hintIn .8s ease 1s both;
}

@keyframes hintIn {
  from { opacity: 0; }
  to { opacity: .8; }
}

.letter-screen.is-opening .env-hint {
  animation: hintOut .3s ease forwards;
}

@keyframes hintOut {
  to { opacity: 0; }
}

/* "tap anywhere to continue" only after the letter is open */
.screen.letter-screen::before {
  transition: opacity .6s ease;
}

.screen.letter-screen:not(.is-open)::before {
  opacity: 0;
}

/* hearts drifting up behind the letter */
.letter-heart {
  position: absolute;
  bottom: -30px;
  z-index: 1;
  font-style: normal;
  line-height: 1;
  pointer-events: none;
  filter: drop-shadow(0 0 6px rgba(255, 80, 145, .45));
}


/* =====================================================================
   ✦ CINEMATIC UPGRADE ✦
   Page transitions · per-page intros · "tap" hints that wait for the
   animation to finish · the new balloon sky.
   (Appended last on purpose: these rules override the older ones.)
   ===================================================================== */

/* ---------- transitions ---------- */

.screen.active {
  animation: none;                 /* transitions are driven from script.js */
}

.screen.tx-in,
.screen.tx-out {
  pointer-events: none;
}

.tx-layer {
  position: absolute;
  inset: 0;
  z-index: 20;
  pointer-events: none;
  overflow: hidden;
}

.tx-ring {
  position: absolute;
  width: 0;
  height: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  border: 3px solid rgba(255, 226, 170, .95);
  box-shadow:
    0 0 26px 6px rgba(255, 140, 190, .75),
    inset 0 0 26px rgba(255, 150, 190, .5);
}

.tx-ring.gold {
  border-color: rgba(255, 236, 190, .95);
  box-shadow:
    0 0 28px 8px rgba(255, 190, 90, .75),
    inset 0 0 28px rgba(255, 190, 90, .45);
}

.tx-vline {
  position: absolute;
  top: 0;
  width: 3px;
  height: 100%;
  margin-left: -1.5px;
  background: linear-gradient(transparent, #fff 18%, #ffd9a8 50%, #fff 82%, transparent);
  box-shadow: 0 0 20px 5px rgba(255, 190, 220, .85);
}

.tx-flash {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(255, 228, 242, .95) 0,
    rgba(255, 140, 190, .38) 38%,
    transparent 72%
  );
}

.tx-diag {
  position: absolute;
  left: 0;
  top: 0;
  width: 5px;
  margin-left: -2px;
  transform-origin: 0 0;
  background: linear-gradient(transparent, #fff 15%, #ffe9b8 50%, #fff 85%, transparent);
  box-shadow:
    0 0 22px 6px rgba(255, 200, 120, .85),
    0 0 60px 16px rgba(255, 120, 180, .45);
}

.tx-hbar {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 5px;
  background: linear-gradient(90deg, transparent, #fff0c4 28%, #fff 50%, #fff0c4 72%, transparent);
  box-shadow:
    0 0 40px 14px rgba(255, 190, 110, .65),
    0 -20px 80px 30px rgba(255, 150, 120, .3);
}

.pop-host {
  position: absolute;
  width: 0;
  height: 0;
  pointer-events: none;
}

@keyframes txFadeUp {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

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

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


/* ---------- "tap" hints: only once the page has finished animating ---------- */

.screen.active:not(.hero)::before {
  opacity: 0;
  transition: opacity .9s ease;
}

.screen.active.ready:not(.hero)::before {
  opacity: 1;
  animation: hintBreath 2.8s ease-in-out 1s infinite;
}

@keyframes hintBreath {
  0%, 100% { opacity: 1; }
  50%      { opacity: .42; }
}

/* last page: the Replay button is the only call to action */
.screen.final-screen::before {
  display: none;
}

/* balloons page has a card + hearts at the bottom, so the hint sits lower */
.screen.reasons-screen.active::before {
  bottom: 16px;
}


/* ---------- 0 · opening ---------- */

.hero:not(.go) .eyebrow {
  opacity: 0;
}

.hero.go .eyebrow {
  animation: txFadeDown 1s ease .15s both;
}

.hero:not(.go) .heart {
  opacity: 0;
}

.hero.go .heart {
  animation:
    heartbeat 1.35s ease-in-out infinite,
    heroPop 1s cubic-bezier(.2, 1.5, .4, 1) .1s both;
}

@keyframes heroPop {
  0%   { scale: 0;   opacity: 0; }
  55%  { scale: 1.22; opacity: 1; }
  100% { scale: 1;   opacity: 1; }
}

.hero:not(.go) .archery-scene {
  opacity: 0;
}

.hero.go .archery-scene {
  animation: txFade 1s ease .8s both;
}

.hero .hint {
  opacity: 0;
  transition: opacity .8s ease;
}

.hero.ready .hint {
  opacity: .65;
}

.hero.aiming .hint {
  opacity: 0;
}


/* ---------- 1 · birthday ---------- */

.birthday .ln {
  display: inline-block;
}

.birthday .ch {
  display: inline-block;
  opacity: 0;
}

.birthday.go .ch {
  animation: chIn .8s cubic-bezier(.2, 1.5, .4, 1) calc(var(--base, .3s) + var(--i) * 55ms) both;
}

.birthday h1 {
  --base: .35s;
}

.birthday .name {
  --base: 1.35s;
  text-shadow: 0 0 34px rgba(240, 167, 44, .45);
}

@keyframes chIn {
  0%   { opacity: 0; transform: translateY(70px) rotate(10deg) scale(.5); filter: blur(10px); }
  60%  { opacity: 1; filter: blur(0); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

.birthday:not(.go) .mini,
.birthday:not(.go) .cat,
.birthday:not(.go) .tiny {
  opacity: 0;
}

.birthday.go .mini {
  animation: txFadeDown .9s ease .05s both;
}

.birthday.go .cat {
  animation:
    catPop .9s cubic-bezier(.2, 1.7, .4, 1) 2.1s both,
    catBob 3s ease-in-out 3.2s infinite;
}

.birthday.go .tiny {
  animation: txFadeUp 1s ease 2.7s both;
}

@keyframes catPop {
  0%   { opacity: 0; transform: scale(0) rotate(-25deg); }
  100% { opacity: 1; transform: none; }
}

@keyframes catBob {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-7px) rotate(2deg); }
}

.birthday-fx {
  left: 50%;
  top: 52%;
  z-index: 3;
}


/* ---------- 2 · letter: envelope arrives only after the page is revealed ---------- */

.letter-screen.active .env-stage {
  animation: none;
  opacity: 0;
}

.letter-screen.go .env-stage {
  animation: envIn .9s cubic-bezier(.2, .9, .25, 1) both;
}

.letter-screen:not(.go) .env-hint {
  animation: none;
  opacity: 0;
}

.letter-screen.go .env-hint {
  animation: hintIn .8s ease 1.15s both;
}

.letter-screen.go.is-opening .env-hint {
  animation: hintOut .3s ease forwards;
}


/* ---------- 4 · cake ---------- */

.cake-screen:not(.go) .cake-wrap > .eyebrow,
.cake-screen:not(.go) .cake-wrap > h2,
.cake-screen:not(.go) .cake-wrap > .sub {
  opacity: 0;
}

.cake-screen.go .cake-wrap > .eyebrow { animation: txFadeDown .8s ease .05s both; }
.cake-screen.go .cake-wrap > h2        { animation: txFadeUp .8s ease .15s both; }
.cake-screen.go .cake-wrap > .sub      { animation: txFadeUp .8s ease .3s both; }

/* the cake's own sub-text animation keeps working */
.cake-screen.go .cake-wrap > .sub.sub-swap {
  animation: subSwap .5s ease both;
}


/* ---------- 5 · tree ---------- */

.tree-screen:not(.go) .eyebrow,
.tree-screen:not(.go) h2,
.tree-screen:not(.go) .tree-text {
  opacity: 0;
}

.tree-screen.go .eyebrow   { animation: txFadeDown .9s ease .1s both; }
.tree-screen.go h2         { animation: txFadeUp .9s ease .25s both; }
.tree-screen.go .tree-text { animation: txFadeUp 1.1s ease 3.2s both; }


/* ---------- 6 · final ---------- */

.final-screen:not(.go) .final-card > * {
  opacity: 0;
}

.final-screen.go .final-card > * {
  animation: txFadeUp .95s cubic-bezier(.2, .8, .25, 1) calc(.2s + var(--k, 0) * .3s) both;
}

.final-card > :nth-child(1) { --k: 0; }
.final-card > :nth-child(2) { --k: 1; }
.final-card > :nth-child(3) { --k: 2; }
.final-card > :nth-child(4) { --k: 3; }
.final-card > :nth-child(5) { --k: 4; }
.final-card > :nth-child(6) { --k: 5; }

.final-screen.go .final-card h1 {
  animation-name: txFadeUp, finalGlow;
  animation-duration: .95s, 3.5s;
  animation-delay: .5s, 1.6s;
  animation-iteration-count: 1, infinite;
  animation-timing-function: cubic-bezier(.2, .8, .25, 1), ease-in-out;
  animation-fill-mode: both, none;
}

@keyframes finalGlow {
  0%, 100% { text-shadow: 0 0 0 rgba(236, 50, 112, 0); }
  50%      { text-shadow: 0 0 36px rgba(236, 50, 112, .35); }
}


/* =====================================================================
   3 · THE BALLOON SKY
   ===================================================================== */

.reasons-screen {
  --bl-w: clamp(58px, 16vw, 90px);
  align-items: flex-start;
}

.reasons-screen .content {
  margin-top: clamp(20px, 5vh, 44px);
}

.reasons-screen .sub {
  margin-bottom: 0;
}

.reasons-screen:not(.go) .content > * {
  opacity: 0;
}

.reasons-screen.go .content > .eyebrow { animation: txFadeDown .9s ease .05s both; }
.reasons-screen.go .content > h2        { animation: txFadeUp .9s ease .2s both; }
.reasons-screen.go .content > .sub      { animation: txFadeUp .9s ease 1.7s both; }

.reasons-screen.go .content > .sub.sub-swap {
  animation: subSwap .5s ease both;
}


/* living background */

.aurora {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.aurora i {
  position: absolute;
  width: 62vmax;
  height: 62vmax;
  border-radius: 50%;
  filter: blur(64px);
  opacity: .34;
  mix-blend-mode: screen;
  transition: opacity 1.6s ease;
  animation: auroraMove 17s ease-in-out infinite alternate;
}

.aurora i:nth-child(1) {
  left: -22vmax;
  top: -20vmax;
  background: radial-gradient(circle, #ff5d91, transparent 65%);
}

.aurora i:nth-child(2) {
  right: -26vmax;
  top: 8vmax;
  background: radial-gradient(circle, #7b5cff, transparent 65%);
  animation-duration: 22s;
  animation-delay: -6s;
}

.aurora i:nth-child(3) {
  left: 6vmax;
  bottom: -32vmax;
  background: radial-gradient(circle, #ffb05c, transparent 65%);
  opacity: .24;
  animation-duration: 26s;
}

.reasons-screen.finale .aurora i {
  opacity: .55;
}

@keyframes auroraMove {
  to { transform: translate(9vmax, -7vmax) scale(1.16); }
}

.dust {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}

.dust i {
  position: absolute;
  bottom: -12px;
  left: var(--l);
  width: var(--s);
  height: var(--s);
  border-radius: 50%;
  background: rgba(255, 218, 236, .8);
  box-shadow: 0 0 8px 2px rgba(255, 170, 210, .5);
  opacity: 0;
  animation: dustUp var(--t) linear var(--d) infinite;
}

@keyframes dustUp {
  0%   { transform: translate(0, 0); opacity: 0; }
  10%  { opacity: .9; }
  90%  { opacity: .55; }
  100% { transform: translate(var(--dx), -105vh); opacity: 0; }
}


/* the sky that holds the balloons */

.sky {
  position: absolute;
  left: 50%;
  top: clamp(168px, 27vh, 250px);
  bottom: 204px;
  width: min(760px, 100%);
  transform: translateX(-50%);
  z-index: 3;
  pointer-events: none;
}

.bl {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--bl-w);
  height: calc(var(--bl-w) * 1.75);
  margin-left: calc(var(--bl-w) / -2);
  padding: 0;
  background: none;
  border: 0;
  outline: none;
  cursor: pointer;
  pointer-events: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  opacity: 0;
}

.reasons-screen.go .bl {
  animation: blRise 1.5s cubic-bezier(.2, .85, .3, 1.08) calc(.35s + var(--k) * .16s) both;
}

.reasons-screen.live .bl {
  pointer-events: auto;
}

@keyframes blRise {
  0%   { opacity: 0; transform: translateY(80vh) scale(.6); }
  70%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}

.bl-float {
  position: absolute;
  inset: 0;
  transform-origin: 50% 35%;
  animation: blDrift var(--dur) ease-in-out var(--delay) infinite;
}

@keyframes blDrift {
  0%, 100% { transform: translate(0, 0) rotate(-2.5deg); }
  25%      { transform: translate(calc(var(--dx) * .6), calc(var(--amp) * -.7)) rotate(1deg); }
  50%      { transform: translate(var(--dx), calc(var(--amp) * -1)) rotate(3deg); }
  75%      { transform: translate(calc(var(--dx) * .3), calc(var(--amp) * -.4)) rotate(-1deg); }
}

.bl-body {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: calc(var(--bl-w) * 1.2);
  display: grid;
  place-items: center;
  border-radius: 50% 50% 50% 50% / 56% 56% 44% 44%;
  background:
    radial-gradient(ellipse 26% 18% at 30% 22%, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0)),
    radial-gradient(circle at 36% 30%, var(--c1), var(--c2) 82%);
  box-shadow:
    inset -8px -12px 20px rgba(0, 0, 0, .2),
    inset 6px 6px 14px rgba(255, 255, 255, .24),
    0 0 38px -4px var(--glow),
    0 18px 28px rgba(0, 0, 0, .28);
  transition: transform .25s cubic-bezier(.3, 1.6, .5, 1);
}

.bl:hover .bl-body {
  transform: scale(1.07);
}

.bl:active .bl-body {
  transform: scale(.93);
}

.bl-heart {
  font-style: normal;
  font-size: calc(var(--bl-w) * .3);
  line-height: 1;
  color: rgba(255, 255, 255, .88);
  text-shadow: 0 0 10px rgba(255, 255, 255, .7);
  animation: blBeat 1.8s ease-in-out infinite;
}

@keyframes blBeat {
  0%, 100% { transform: scale(1); }
  45%      { transform: scale(1.22); }
}

.bl-knot {
  position: absolute;
  left: 50%;
  top: calc(var(--bl-w) * 1.2 - 3px);
  width: 11px;
  height: 10px;
  margin-left: -5.5px;
  background: var(--c2);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

.bl-string {
  position: absolute;
  left: 50%;
  top: calc(var(--bl-w) * 1.2 + 6px);
  width: 1.5px;
  height: calc(var(--bl-w) * .52);
  margin-left: -.75px;
  background: linear-gradient(rgba(255, 255, 255, .7), rgba(255, 255, 255, .08));
  transform-origin: 50% 0;
  animation: strSway 2.6s ease-in-out var(--delay) infinite;
}

@keyframes strSway {
  0%, 100% { transform: rotate(-7deg); }
  50%      { transform: rotate(8deg); }
}

/* popped: the body bursts, the string flutters down */

.bl.popped {
  pointer-events: none;
}

.bl.popped .bl-float {
  animation-play-state: paused;
}

.bl.popped .bl-body {
  animation: blPop .2s ease-out forwards;
}

.bl.popped .bl-heart {
  animation: none;
}

.bl.popped .bl-knot,
.bl.popped .bl-string {
  animation: blStringFall 1.1s cubic-bezier(.4, 0, 1, .6) forwards;
}

@keyframes blPop {
  0%   { transform: scale(1);          opacity: 1; }
  35%  { transform: scale(1.28, 1.18); opacity: 1; }
  100% { transform: scale(1.55);       opacity: 0; }
}

@keyframes blStringFall {
  0%   { transform: translateY(0) rotate(0deg);     opacity: 1; }
  100% { transform: translateY(170px) rotate(40deg); opacity: 0; }
}


/* pop effects layer */

.reason-fx {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  overflow: hidden;
}

.pop-flash {
  position: absolute;
  left: 0;
  top: 0;
  width: 150px;
  height: 150px;
  border-radius: 50%;
}

.pop-ring {
  position: absolute;
  left: 0;
  top: 0;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: 3px solid var(--rc, #fff);
  box-shadow: 0 0 14px var(--rc, #fff);
}

.pop-shard {
  position: absolute;
  left: 0;
  top: 0;
}

.pop-pulse {
  position: absolute;
  inset: 0;
}

.msg-orb {
  position: absolute;
  left: 0;
  top: 0;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0 24%, var(--c1) 55%, transparent 74%);
  box-shadow: 0 0 18px 6px var(--c1), 0 0 44px 14px var(--c2);
}

.msg-trail {
  position: absolute;
  left: 0;
  top: 0;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--c1);
  box-shadow: 0 0 10px 3px var(--c2);
}


/* the message card */

.reason-stage {
  position: absolute;
  left: 50%;
  bottom: 88px;
  width: min(470px, 90vw);
  height: 112px;
  transform: translateX(-50%);
  z-index: 5;
  pointer-events: none;
}

.reason-card {
  --a: 0deg;
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 12px 22px;
  border-radius: 22px;
  text-align: center;
  background: linear-gradient(160deg, rgba(255, 255, 255, .17), rgba(255, 255, 255, .05));
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow:
    0 18px 50px rgba(0, 0, 0, .35),
    0 0 64px -10px var(--c2),
    inset 0 0 30px rgba(255, 255, 255, .05);
  animation: rcIn .85s cubic-bezier(.2, 1.3, .35, 1) both;
}

/* a bright comet that circles the card's edge */
.reason-card::before {
  content: "";
  position: absolute;
  inset: -1.5px;
  padding: 1.5px;
  border-radius: 23px;
  background: conic-gradient(
    from var(--a),
    rgba(255, 255, 255, .16) 0 50%,
    var(--c2) 76%,
    #fff 92%,
    rgba(255, 255, 255, .16)
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box exclude,
    linear-gradient(#000 0 0);
  animation: rcSpin 3.2s linear infinite;
  pointer-events: none;
}

@property --a {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@keyframes rcSpin {
  to { --a: 360deg; }
}

.rc-tag {
  font-size: 10px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--c1);
  opacity: .95;
}

.rc-text {
  margin: 0;
  font-family: "Playfair Display", serif;
  font-size: clamp(17px, 4.6vw, 22px);
  line-height: 1.3;
  color: #fff4ec;
}

.rc-text .w {
  display: inline-block;
  opacity: 0;
  animation: rcWord .65s ease calc(.3s + var(--i) * 80ms) both;
}

@keyframes rcIn {
  from { opacity: 0; transform: scale(.45); filter: blur(8px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

@keyframes rcWord {
  from { opacity: 0; transform: translateY(10px); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

.reason-card.out {
  animation: rcOut .38s ease forwards;
}

@keyframes rcOut {
  to { opacity: 0; transform: translateY(-26px) scale(.92); filter: blur(6px); }
}


/* hearts that fill up */

.progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 48px;
  display: flex;
  justify-content: center;
  gap: 15px;
  z-index: 4;
  pointer-events: none;
}

.pip {
  font-style: normal;
  font-size: 21px;
  line-height: 1;
  color: rgba(255, 255, 255, .28);
  transition: color .4s ease, text-shadow .4s ease;
}

.pip.on {
  color: var(--c2);
  text-shadow: 0 0 14px var(--c1), 0 0 4px #fff;
  animation: pipPop .65s cubic-bezier(.2, 1.8, .4, 1) both;
}

.reasons-screen.finale .pip.on {
  animation: pipWave 1.5s ease-in-out calc(var(--n) * .12s) infinite;
}

@keyframes pipPop {
  0%   { transform: scale(.2); }
  60%  { transform: scale(1.7); }
  100% { transform: scale(1); }
}

@keyframes pipWave {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(-7px) scale(1.25); }
}


/* short phones */

@media (max-height: 640px) {

  .sky {
    bottom: 188px;
  }

  .reason-stage {
    height: 100px;
    bottom: 80px;
  }

  .progress {
    bottom: 42px;
  }

}


/* respect people who prefer less motion */

@media (prefers-reduced-motion: reduce) {

  .aurora i,
  .dust i,
  .bl-float,
  .bl-string {
    animation: none;
  }

}


/* while a message is being delivered, the other balloons wait their turn */

.reasons-screen.busy .bl {
  pointer-events: none;
}

.reasons-screen .bl-body {
  transition:
    transform .25s cubic-bezier(.3, 1.6, .5, 1),
    filter .5s ease;
}

.reasons-screen.busy .bl:not(.popped) .bl-body {
  filter: saturate(.8) brightness(.82);
}

.rc-text {
  text-wrap: balance;
}
