:root {
  --bg: #e4eedd;
  --bg-deep: #cfe2c4;
  --paper: #f1f0e9;
  --ink: #34573e;          /* dark green text + photo outline */
  --ink-soft: #5d7a63;
  --green: #a9d095;        /* light green torn paper */
  --green-glitter: #7fb46b;
  --silver: #c3c7cd;
  --seal: #a8362f;

  --hand: "Gochi Hand", "Comic Sans MS", cursive;
  --serif: "Fraunces", Georgia, serif;
  --foil: "Fredoka", system-ui, sans-serif;
  --sans: "Jost", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body { height: 100%; overscroll-behavior: none; }

body {
  margin: 0;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background:
    radial-gradient(1200px 600px at 50% -10%, #f6faf2 0%, transparent 60%),
    radial-gradient(800px 500px at 100% 110%, var(--bg-deep) 0%, transparent 60%),
    var(--bg);
  color: var(--ink);
  font-family: var(--serif);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

[hidden] { display: none !important; }

#confetti {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 50;
}

/* ---------- Intro / envelope ---------- */

.intro {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 24px 16px;
  transition: opacity .6s ease, transform .6s ease;
}

.intro.leaving {
  opacity: 0;
  transform: scale(1.04);
  pointer-events: none;
}

.intro-hint {
  margin: 0;
  font-family: var(--hand);
  font-size: clamp(2.2rem, 9vw, 3.2rem);
  color: var(--ink);
  transform: rotate(-3deg);
}

.intro-tap {
  margin: 0;
  font-family: var(--sans);
  font-size: .8rem;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--ink-soft);
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  50% { opacity: .35; }
}

.envelope {
  position: relative;
  width: min(340px, 82vw);
  aspect-ratio: 4 / 3;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  perspective: 1200px;
  animation: hover 3.2s ease-in-out infinite;
  -webkit-tap-highlight-color: transparent;
}

.envelope:focus-visible { outline: 2px solid var(--ink); outline-offset: 10px; border-radius: 8px; }

@keyframes hover {
  50% { transform: translateY(-8px) rotate(-1deg); }
}

.envelope.open { animation: none; cursor: default; }

.envelope > span { position: absolute; display: block; }

.env-back {
  inset: 0;
  background: #d6e7cb;
  border-radius: 6px;
  box-shadow: 0 25px 50px -20px rgba(50, 80, 50, .35);
}

.env-letter {
  left: 7%;
  right: 7%;
  top: 6%;
  bottom: 6%;
  background: var(--paper);
  border-radius: 4px;
  z-index: 2;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 10%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .06);
  transition: transform .7s cubic-bezier(.2, .8, .2, 1) .75s;
}

.letter-script {
  font-family: var(--hand);
  font-size: clamp(1.5rem, 6vw, 2rem);
  color: var(--ink);
}

.env-front {
  inset: 0;
  z-index: 3;
  background: linear-gradient(160deg, #f7faf3, #e6efe0);
  clip-path: polygon(0 0, 50% 54%, 100% 0, 100% 100%, 0 100%);
  border-radius: 6px;
}

.env-flap {
  inset: 0;
  z-index: 4;
  background: linear-gradient(180deg, #c3dcb3, #b5d3a2);
  clip-path: polygon(0 0, 100% 0, 50% 58%);
  transform-origin: top center;
  transition: transform .6s ease .15s, z-index 0s linear .45s;
}

.seal {
  z-index: 5;
  left: 50%;
  top: 52%;
  width: 54px;
  height: 54px;
  margin: -27px 0 0 -27px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #cf5a50, var(--seal) 60%, #7e2621);
  color: #fbe9e5;
  font-size: 22px;
  line-height: 54px;
  text-align: center;
  box-shadow: 0 3px 8px rgba(110, 30, 25, .4), inset 0 0 0 4px rgba(255, 255, 255, .12);
  transition: opacity .25s ease, transform .25s ease;
}

.envelope.open .seal { opacity: 0; transform: scale(.6); }
.envelope.open .env-flap { transform: rotateX(180deg); z-index: 1; }
.envelope.open .env-letter { transform: translateY(-42%); }

/* ---------- Invitation poster ----------
   Everything inside is sized in cqw (1% of the poster's width) so the
   whole poster scales like an image. Height = 164cqw. */

.invite {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 20px 16px;
  position: relative;
  z-index: 2;
}

.stage {
  flex: 1;
  min-height: 0;
  width: 100%;
  display: grid;
  place-items: center;
  container-type: size;
}

.poster {
  position: relative;
  width: min(480px, 100cqw, 100cqh * 100 / 164);
  aspect-ratio: 100 / 164;
  container-type: inline-size;
  overflow: hidden;
  background: var(--paper);
  border-radius: 6px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, .05), 0 40px 70px -30px rgba(40, 70, 40, .45);
  animation: posterIn .9s cubic-bezier(.2, .8, .2, 1) both;
}

@keyframes posterIn {
  from { opacity: 0; transform: translateY(30px) rotate(-1.5deg) scale(.97); }
}

@media (max-width: 520px) {
  .invite { padding: 0 0 env(safe-area-inset-bottom); }
  .poster { border-radius: 0; box-shadow: none; }
}

.layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.layer-back { z-index: 0; }
.layer-front { z-index: 3; }

.poster > :not(.layer) { position: absolute; margin: 0; z-index: 4; }

/* fade each piece in, one after another */
.poster > :not(.layer):not(.photo) { animation: popIn .7s cubic-bezier(.2, .9, .3, 1.3) backwards; }
.poster > .title { animation-delay: .35s; }
.poster > .hero-row { animation-delay: .55s; }
.poster > .when { animation-delay: .75s; }
.poster > .message { animation-delay: .85s; }
.poster > .info { animation-delay: .95s; }
.poster > .see-you { animation-delay: 1.3s; }

@keyframes popIn {
  from { opacity: 0; transform: translateY(3cqw) scale(.94); }
}

.title {
  top: 11cqw;
  right: 3cqw;
  width: 58cqw;
  font-family: var(--hand);
  font-weight: 400;
  font-size: 15cqw;
  line-height: .95;
  text-align: center;
  color: var(--ink);
  rotate: -5deg;
  cursor: pointer;
}

.hero-row {
  top: 42cqw;
  left: 20cqw;
  right: 6cqw;
  height: 28cqw;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3cqw;
}

.cake {
  width: 25cqw;
  flex: none;
  rotate: -4deg;
  /* white sticker edge like a cut-out clipping */
  filter:
    drop-shadow(.5cqw 0 0 #fff) drop-shadow(-.5cqw 0 0 #fff)
    drop-shadow(0 .5cqw 0 #fff) drop-shadow(0 -.5cqw 0 #fff)
    drop-shadow(.4cqw .8cqw .6cqw rgba(0, 0, 0, .25));
}
.hero-row:not(:has(.age-wrap:not([hidden]))) .cake { width: 33cqw; }

.flames path { transform-box: fill-box; transform-origin: 50% 100%; animation: flicker 1.4s ease-in-out infinite alternate; }
.flames path:nth-child(2n) { animation-delay: -.6s; }
@keyframes flicker { to { transform: scale(.85, 1.12) rotate(4deg); } }

/* silver foil balloon numbers */
.age {
  font-family: var(--foil);
  font-weight: 700;
  font-size: 31cqw;
  line-height: 1;
  letter-spacing: -1.5cqw;
  color: transparent;
  background:
    repeating-linear-gradient(100deg, rgba(255, 255, 255, 0) 0 2.2cqw, rgba(255, 255, 255, .55) 2.6cqw, rgba(255, 255, 255, 0) 3.4cqw 5cqw),
    linear-gradient(160deg, #7d848d 0%, #f4f5f7 22%, #a9afb7 38%, #ffffff 50%, #8f969f 66%, #e8eaed 80%, #6f767f 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-stroke: .45cqw #8a9199;
  filter: drop-shadow(.6cqw 1cqw .8cqw rgba(0, 0, 0, .3));
}

.age-wrap {
  position: relative;
  display: inline-block;
  rotate: 3deg;
  animation: bob 3s ease-in-out infinite;
}


@keyframes bob {
  50% { translate: 0 -1.2cqw; rotate: 1deg; }
}

.when {
  top: 72cqw;
  left: 30cqw;
  right: 5cqw;
  font-family: var(--serif);
  font-weight: 800;
  font-size: 8.6cqw;
  letter-spacing: -.3cqw;
  text-align: center;
  white-space: nowrap;
}
.when .bar { font-weight: 600; opacity: .8; margin: 0 1.6cqw; }

.message {
  top: 83cqw;
  left: 30cqw;
  right: 5cqw;
  font-family: var(--serif);
  font-weight: 600;
  font-size: 3.9cqw;
  text-align: center;
}

/* location, street and dress code stack in one column */
.info {
  top: 91cqw;
  left: 48cqw;
  right: 3cqw;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2.2cqw;
  text-align: center;
}
.info > * { margin: 0; }

.where {
  font-family: var(--serif);
  font-weight: 800;
  font-size: 6.6cqw;
  letter-spacing: -.2cqw;
  line-height: 1.05;
}
.venue { display: block; }

.address {
  font-family: var(--hand);
  font-size: 5.4cqw;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink);
  text-decoration: none;
}
.address::before { content: "📍"; margin-right: .6cqw; }

.dress {
  margin-top: 1.5cqw !important;
  font-family: var(--serif);
  font-weight: 800;
  font-size: 5.6cqw;
  text-align: center;
  line-height: 1.1;
}

.dress-color {
  display: block;
  margin-top: .8cqw;
  font-family: var(--hand);
  font-weight: 400;
  font-size: 6.4cqw;
  text-transform: uppercase;
}

.swatch {
  display: inline-block;
  width: 4cqw;
  height: 4cqw;
  margin-right: 1.4cqw;
  vertical-align: -.2cqw;
  border-radius: 50%;
  background: #fff;
  border: .35cqw solid var(--ink);
}

.see-you {
  top: 135cqw;
  left: 60cqw;
  font-family: var(--hand);
  font-size: 5cqw;
  line-height: 1.15;
  text-transform: uppercase;
  rotate: -3deg;
}

/* the birthday girl, bleeding off the bottom-left corner */
.poster > .photo {
  left: -15cqw;
  bottom: -25cqw;
  width: 100cqw;
  padding: 0;
  border: 0;
  background: none;
  z-index: 2;
  animation: slideIn 1s cubic-bezier(.2, .9, .3, 1.1) .5s backwards;
}

.photo { perspective: 220cqw; }

/* only one side is ever shown: the card turns edge-on, the photo swaps,
   then it turns back (see flipPhoto in script.js) */
.flip {
  display: block;
  position: relative;
}

.face { display: block; }
.face img { display: block; }
.face-then img { width: 100%; height: auto; }

.face-now {
  position: absolute;
  inset: 0;
  visibility: hidden;
}
.photo.flipped .face-now { visibility: visible; }
.photo.flipped .face-then { visibility: hidden; }
.face-now img {
  position: absolute;
  left: 7%;
  bottom: 0;
  height: 100%;
  width: auto;
}

@keyframes slideIn {
  from { opacity: 0; transform: translate(-12cqw, 14cqw) rotate(-6deg); }
}


/* ---------- Fun bits ---------- */

/* the disco ball spins in its own layer, light spots sweep over everything */
.layer-disco { z-index: 0; }
.spots { z-index: 5; }

.spot {
  position: absolute;
  left: 0;
  top: var(--y);
  width: var(--s);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 250, 222, 1) 0 16%, rgba(247, 204, 92, .8) 34%, rgba(240, 190, 70, .25) 52%, rgba(240, 190, 70, 0) 70%);
  filter: blur(.3cqw);
  opacity: 0;
  animation: sweep var(--dur) linear var(--delay) infinite;
}

@keyframes sweep {
  0% { transform: translate(-12cqw, 0); opacity: 0; }
  12%, 88% { opacity: var(--o); }
  100% { transform: translate(112cqw, var(--dy)); opacity: 0; }
}

/* candles: blown out and relit every 3 seconds */
.flames path { transition: transform .3s ease, opacity .3s ease; }
.cake.out .flames path { animation: none; transform: scale(0, .2); opacity: 0; }
.cake.out .flames path:nth-child(2) { transition-delay: .15s; }
.cake.out .flames path:nth-child(3) { transition-delay: .3s; }
.cake.out .flames path:nth-child(4) { transition-delay: .45s; }
.cake.out .flames path:nth-child(5) { transition-delay: .6s; }

.smoke path { opacity: 0; transform-box: fill-box; transform-origin: 50% 100%; }
.cake.out .smoke path { animation: smoke 1.8s ease-out forwards; }
.cake.out .smoke path:nth-child(2) { animation-delay: .15s; }
.cake.out .smoke path:nth-child(3) { animation-delay: .3s; }
.cake.out .smoke path:nth-child(4) { animation-delay: .45s; }
.cake.out .smoke path:nth-child(5) { animation-delay: .6s; }

@keyframes smoke {
  0% { opacity: 0; transform: translateY(2px) scaleY(.6); }
  25% { opacity: .85; }
  100% { opacity: 0; transform: translateY(-9px) scaleY(1.15); }
}

.wish {
  top: 34cqw;
  left: 3cqw;
  font-family: var(--hand);
  font-size: 5.6cqw;
  color: var(--ink);
  rotate: -8deg;
  text-shadow: .4cqw .4cqw 0 #fff, -.4cqw -.4cqw 0 #fff, .4cqw -.4cqw 0 #fff, -.4cqw .4cqw 0 #fff;
}

/* the "!!!" take turns jumping and wiggling */
.bangs span {
  display: inline-block;
  transform-origin: 50% 90%;
  animation: bang 1.8s ease-in-out infinite;
}
.bangs span:nth-child(2) { animation-delay: .15s; }
.bangs span:nth-child(3) { animation-delay: .3s; }

@keyframes bang {
  0%, 45%, 100% { transform: none; }
  55% { transform: translateY(-2.6cqw) rotate(-22deg) scale(1.15); }
  68% { transform: translateY(0) rotate(16deg); }
  80% { transform: rotate(-8deg); }
  90% { transform: rotate(3deg); }
}

/* each star pops and spins one point-turn (72°) on its own rhythm —
   a five-point star looks the same at 72°, so the loop restarts seamlessly */
.twinkle {
  transform-box: fill-box;
  transform-origin: center;
  animation: twinkle var(--tw, 3s) cubic-bezier(.3, .7, .4, 1) var(--twd, 0s) infinite;
}

@keyframes twinkle {
  0%, 55% { transform: none; }
  68% { transform: scale(1.6) rotate(40deg); }
  80% { transform: scale(.85) rotate(64deg); }
  90% { transform: scale(1.12) rotate(72deg); }
  100% { transform: rotate(72deg); }
}

/* four-point sparkles (on the mirrorball) turn a quarter instead */
.twinkle.four { animation-name: twinkleFour; }

@keyframes twinkleFour {
  0%, 55% { transform: none; }
  68% { transform: scale(1.7) rotate(50deg); }
  82% { transform: scale(.8) rotate(80deg); }
  92% { transform: scale(1.1) rotate(90deg); }
  100% { transform: rotate(90deg); }
}

/* the hand-drawn star isn't symmetric (hatching), so it pops and swings back */
.twinkle.swing { animation-name: twinkleSwing; }

@keyframes twinkleSwing {
  0%, 55%, 100% { transform: none; }
  68% { transform: scale(1.5) rotate(28deg); }
  80% { transform: scale(.9) rotate(-14deg); }
  90% { transform: scale(1.06) rotate(5deg); }
}

/* glints flash in the gaps between stars */
.glint {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: glint var(--tw, 2.4s) ease-in-out var(--twd, 0s) infinite;
}

@keyframes glint {
  0%, 50%, 100% { opacity: 0; transform: scale(.1) rotate(0); }
  65% { opacity: 1; transform: scale(1.25) rotate(45deg); }
  80% { opacity: .9; transform: scale(.9) rotate(80deg); }
  92% { opacity: 0; transform: scale(.3) rotate(90deg); }
}

/* ---------- Calendar / RSVP stickers on the bottom-right paper ---------- */

.poster > .stickers {
  right: 4cqw;
  bottom: 3.5cqw;
  display: flex;
  flex-direction: row-reverse;
  align-items: flex-end;
  gap: 2cqw;
  animation-delay: 1.45s;
}

.sticker {
  appearance: none;
  display: inline-block;
  margin: 0;
  padding: 1.4cqw 3cqw 1.6cqw;
  font-family: var(--hand);
  font-size: 4.6cqw;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  background: #fffdf6;
  border: 0;
  border-radius: .8cqw;
  box-shadow: .3cqw .6cqw 1.2cqw -.2cqw rgba(30, 50, 30, .35);
  rotate: -4deg;
  cursor: pointer;
  transition: transform .15s ease;
}
.sticker:nth-child(2) { rotate: 3deg; }
.sticker:active { transform: scale(.94); }

/* little strip of tape holding the sticker on */
.sticker::before {
  content: "";
  position: absolute;
  top: -1.4cqw;
  left: 50%;
  width: 8cqw;
  height: 2.6cqw;
  margin-left: -4cqw;
  background: rgba(255, 255, 255, .6);
  box-shadow: 0 0 .3cqw rgba(0, 0, 0, .08);
  rotate: 3deg;
}
.sticker { position: relative; }

/* ---------- Floating balloons (around the poster on wide screens) ---------- */

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

.balloon {
  position: absolute;
  bottom: -160px;
  width: var(--size, 60px);
  aspect-ratio: 5 / 6;
  border-radius: 50% 50% 48% 48% / 55% 55% 45% 45%;
  background: radial-gradient(circle at 32% 28%, #fff 0%, #fff 12%, var(--tint, #eef5ea) 55%, var(--edge, #cfe2c6) 100%);
  box-shadow: inset -6px -8px 14px rgba(90, 130, 85, .14);
  opacity: 0;
  animation: floatUp var(--dur, 18s) linear var(--delay, 0s) infinite;
}

.balloon::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: 90px;
  background: linear-gradient(rgba(95, 140, 90, .5), transparent);
}

@keyframes floatUp {
  0% { transform: translate(0, 0) rotate(-4deg); opacity: 0; }
  8% { opacity: .9; }
  50% { transform: translate(var(--drift, 30px), -60vh) rotate(4deg); }
  92% { opacity: .9; }
  100% { transform: translate(0, -125vh) rotate(-4deg); opacity: 0; }
}

@media (max-width: 640px) {
  .balloon { --size: 44px !important; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    transition-delay: 0s !important;
  }
  .balloons { display: none; }
}

/* ---------- Nothing can be selected, dragged or long-pressed ---------- */

body {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;   /* no "Save image / Copy" menu on iPhone long-press */
  -webkit-tap-highlight-color: transparent;
}

img, svg {
  -webkit-user-drag: none;
  user-drag: none;
}
