/* ─────────────────────────────────────────────────────────
   Kevin & Goretti — Wedding Donation Site
   Soft & romantic: warm cream, blush, sage
   Type: Cormorant Garamond (display) + DM Sans (body)
   ───────────────────────────────────────────────────────── */

:root {
  /* Motion curves — a small house style for easings so transitions feel
     considered and consistent (rather than the default 'ease' everywhere).
     - ease-tactile: smooth deceleration, slightly springy. Good for the
       primary motion: card slides, panel reveals, envelope settling.
     - ease-snap:    snappier feedback for buttons, hovers, small UI.
     - ease-soft:    long, gentle ease-in-out for atmospheric motion. */
  --ease-tactile: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-snap:    cubic-bezier(0.32, 0.72, 0, 1);
  --ease-soft:    cubic-bezier(0.65, 0, 0.35, 1);

  /* Palette — set as CSS vars so Tweaks can swap them */
  --cream:        #f6efe3;
  --cream-deep:   #ede3d2;
  --ink:          #3d2f24;
  --ink-soft:     #6b5a4a;
  --ink-mute:     #a6957f;
  --blush:        #e2b9b1;
  --blush-deep:   #c89089;
  --sage:         #9eaa8c;
  --sage-deep:    #7a8a68;
  --gold:         #c9a25b;
  --gold-deep:    #a37f3d;
  --paper:        #fbf7ee;
  --line:         rgba(61, 47, 36, 0.14);

  /* Per-charity tints */
  --c-bbrf:       #b7a3c9;   /* lavender — brain/research */
  --c-bbrf-deep:  #8975a2;
  --c-kab:        #9eaa8c;   /* sage — environment */
  --c-kab-deep:   #6f7d5b;
  --c-dwb:        #d39689;   /* terracotta — global health */
  --c-dwb-deep:   #a96253;

  --font-display: "Cormorant Garamond", "Cormorant", Georgia, serif;
  --font-body:    "DM Sans", -apple-system, system-ui, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  min-height: 100vh;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body {
  /* subtle warm paper grain */
  background-image:
    radial-gradient(ellipse at 20% 10%, rgba(226,185,177,0.18), transparent 55%),
    radial-gradient(ellipse at 85% 90%, rgba(158,170,140,0.16), transparent 55%),
    linear-gradient(180deg, var(--cream) 0%, var(--paper) 100%);
  background-attachment: fixed;
}

/* Typography */
.serif      { font-family: var(--font-display); font-weight: 400; }
.serif-it   { font-family: var(--font-display); font-style: italic; font-weight: 400; }
.mono       { font-family: var(--font-mono); }

h1, h2, h3 { font-family: var(--font-display); font-weight: 400; letter-spacing: 0.005em; }

.eyebrow {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ── Layout shell ─────────────────────────────────────── */
#root {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.scene {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Generous top padding so tall content (e.g. the three-charity choose
     screen, or the totals tiles) doesn't run under the fixed "Kevin & Goretti"
     brand mark in the top-left or the "Raised together" mini in the top-right.
     Bottom padding is smaller because the only thing below is the admin link. */
  padding: clamp(88px, 11vw, 132px) clamp(16px, 4vw, 48px) clamp(48px, 6vw, 80px);
  position: relative;
}
@media (max-width: 640px) {
  .scene { padding: 64px 14px 40px; }
  .brand { font-size: 15px; top: 18px; left: 18px; }
  .totals-mini { top: 18px; right: 18px; font-size: 9.5px; }
  .totals-mini b { font-size: 15px; }
}

.brand {
  position: fixed;
  top: 24px; left: 24px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
  z-index: 10;
  pointer-events: none;
}
.brand .amp { color: var(--blush-deep); margin: 0 4px; }

.totals-mini {
  position: fixed;
  top: 24px; right: 24px;
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  z-index: 10;
}
.totals-mini b {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  letter-spacing: 0;
  text-transform: none;
  font-weight: 500;
  color: var(--ink);
}

/* ── Envelope scene ───────────────────────────────────── */
.intro {
  text-align: center;
  max-width: 720px;
  position: relative;
}

/* Kicker text — lives at the BACK of the stack. Positioned absolutely
   above the envelope but behind it in z-order, so the envelope/card
   naturally cover it during the opening sequence. */
.intro .kicker {
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  z-index: -1;
  opacity: 0;
  animation: fade-in 0.9s 0.2s forwards;
  pointer-events: none;
  white-space: nowrap;
}

.intro-opening .kicker {
  opacity: 0 !important;
  transform: translateX(-50%) translateY(-12px);
  transition: opacity 0.6s 0.4s ease, transform 0.6s 0.4s ease;
  animation: none;
}
.intro-opening .envelope {
  /* envelope stays solid (was opacity 0.35 — too ghostly with petals
     visible through it). A very slight softening (0.95) is enough to
     defer visual weight to the emerging card without making the
     envelope feel translucent. */
  transition: opacity 0.8s 1.0s var(--ease-tactile),
              transform 0.8s 1.0s var(--ease-tactile);
  opacity: 0.95;
  transform: translateY(8px) scale(0.96);
  pointer-events: none;
}

/* Dim backdrop is now baked into the focused card's box-shadow spread,
   so the card itself is never dimmed. Keep the element but make it inert. */
.dim-backdrop {
  display: none;
}

/* When the card is focused, lift its parent above the dim-backdrop.
   Perspective on .envelope-wrap creates a stacking context that
   would otherwise trap the card's z-index INSIDE the wrapper. */
.intro-focused .envelope-wrap {
  z-index: 100;
  position: relative;
}

/* Soften (don't fully dim) the brand + admin link so attention is on the card. */
.intro-focused ~ .brand,
body:has(.intro-focused) .brand,
body:has(.intro-focused) [href="admin.html"] {
  opacity: 0.22;
  transition: opacity 0.7s ease;
}

/* Old kicker rule removed — styled at top of file */


.envelope-wrap {
  position: relative;
  width: min(420px, 80vw);
  margin: 0 auto;
  transform-style: preserve-3d;
  transition: transform 1.1s var(--ease-tactile);
  /* Prevent the swipe gesture from accidentally selecting envelope text
     (name on the front, K&G, postmark text, etc.) or triggering the iOS
     long-press callout. Applied here so it cascades to every face. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Camera follow: as the card emerges, the envelope-wrap slides down so the
   rising card stays in view. The shift must be large enough that the
   envelope clears the card vertically once the card is at viewport center. */
.intro-p-out .envelope-wrap,
.intro-p-focused .envelope-wrap {
  transform: translateY(45vh);
}
/* Drop shadow under the envelope. Responds to the React-driven tilt
   variables so the shadow shifts opposite to the envelope's parallax
   tilt, grows + blurs when the envelope lifts on hover, and stays put
   when the envelope tilts away (simulating a fixed overhead light). */
.envelope-wrap::after {
  content: "";
  position: absolute;
  left: 8%; right: 8%;
  bottom: -28px;
  height: 36px;
  background: radial-gradient(ellipse at center, rgba(61,47,36,0.28), transparent 72%);
  /* Shadow translates OPPOSITE to envelope tilt (parallax effect) and
     scales/blurs/softens with the lift. */
  transform:
    translate3d(
      calc(var(--tilt-x, 0px) * -0.55),
      calc(var(--tilt-y, 0px) * 0.45 + var(--tilt-lift, 0) * 6px),
      0
    )
    scale(calc(1 + var(--tilt-lift, 0) * 0.18));
  filter: blur(calc(8px + var(--tilt-lift, 0) * 6px));
  opacity: calc(0.95 - var(--tilt-lift, 0) * 0.22);
  z-index: -1;
  pointer-events: none;
  transition:
    transform 0.32s cubic-bezier(.22, 1, .36, 1),
    filter 0.32s cubic-bezier(.22, 1, .36, 1),
    opacity 0.32s ease-out;
}

.envelope {
  position: relative;
  width: 100%;
  aspect-ratio: 8 / 5;
  cursor: pointer;
  transform-origin: center center;
  transform-style: preserve-3d;
  transition: transform 0.22s var(--ease-tactile);
  will-change: transform;
  z-index: 3;
}
.envelope.hovered {
  /* shadow handled by .envelope-wrap pseudo-element */
}
.envelope.opening {
  cursor: default;
  pointer-events: none;
  transition: transform 0.7s var(--ease-tactile);
}

/* Flipper hosts the gesture-driven 2D flip. Front and back faces are stacked
   at the same position; rotateY + scaleX (driven by CSS vars set in React)
   creates a card-flip illusion, and opacity (also from CSS vars) swaps which
   face is visible at the 90° midpoint. No 3D backface tricks — reliable
   across browsers and doesn't conflict with the parallax tilt. */
.env-flipper {
  position: absolute;
  inset: 0;
}

/* When the gesture-flip is in any state other than 'rest', apply the live
   rotation + squeeze from the CSS variables React sets on .envelope. */
.envelope.gflip .env-flipper {
  transform: rotateY(var(--flip-rot-y, 0deg)) scaleX(var(--flip-scale-x, 1));
  transition: transform 0.46s var(--ease-tactile), filter 0.46s var(--ease-tactile);
  /* Dim the envelope as it turns toward edge-on. A real object catches
     less light when its surface is perpendicular to the viewer, so a
     parabolic brightness dip (peak at frac ±0.5) sells "3D object spinning"
     rather than "infinitely flat sheet rotating in 2D". */
  filter: brightness(calc(1 - var(--flip-edge-dark, 0) * 0.22));
}
.envelope.gflip-drag .env-flipper {
  /* While the finger is down, follow it 1:1 with no easing. */
  transition: none;
}
/* Hard inner shadow along both vertical sides during the flip — reads
   as the paper's edge being shaded at the rotation angle, adding to the
   sense of thickness. The inset shadow gets stronger toward the midpoint. */
.envelope.gflip .env-face {
  box-shadow: inset 0 0 0 0 transparent;
  transition: box-shadow 0.46s var(--ease-tactile);
}
.envelope.gflip .env-face::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 4px;
  background: linear-gradient(
    90deg,
    rgba(0, 0, 0, calc(var(--flip-edge-dark, 0) * 0.28)) 0%,
    transparent 14%,
    transparent 86%,
    rgba(0, 0, 0, calc(var(--flip-edge-dark, 0) * 0.28)) 100%
  );
  transition: background 0.2s var(--ease-tactile);
  z-index: 6;
}

.env-face {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  overflow: visible;
  opacity: 1;
}
/* Phase-based face swap (when no gesture flip is happening). */
.envelope.phase-front .env-back {
  opacity: 0;
  pointer-events: none;
}
.envelope:not(.phase-front) .env-front {
  opacity: 0;
  pointer-events: none;
}
/* While the user is gesture-flipping, opacity is driven by the React-set
   vars instead. These rules override phase-based ones via specificity. */
.envelope.gflip .env-front { opacity: var(--flip-show-front, 1); }
.envelope.gflip .env-back  { opacity: var(--flip-show-back,  0); }
.envelope.gflip .env-face  { transition: opacity 0s; }
/* Once opening, the back must be visible — no fade gap. */
.envelope.opening .env-back { opacity: 1 !important; transition: none; }

/* ── Front face ───────────────────────────────────────── */
.env-front-paper {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background:
    radial-gradient(ellipse at 20% 30%, rgba(255,255,255,0.45), transparent 55%),
    radial-gradient(ellipse at 80% 80%, rgba(0,0,0,0.04), transparent 55%),
    linear-gradient(180deg, #f3dfd4 0%, #ebc9bb 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.55),
    inset 0 -1px 0 rgba(0,0,0,0.05),
    inset 0 0 0 1px rgba(120,70,55,0.10);
}

.env-return {
  position: absolute;
  top: 8%;
  left: 7%;
  max-width: 45%;
  z-index: 2;
}
.env-return-name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(11px, 1.45vw, 14px);
  color: var(--ink);
  line-height: 1.1;
}
.env-return-addr {
  font-family: var(--font-body);
  font-size: clamp(8px, 1vw, 9.5px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(61,47,36,0.5);
  margin-top: 3px;
}

.env-stamp {
  position: absolute;
  top: 6%;
  right: 6%;
  width: clamp(48px, 11%, 64px);
  z-index: 2;
  transform: rotate(4deg);
  filter: drop-shadow(0 1px 1px rgba(0,0,0,0.10));
}
.env-stamp svg {
  display: block;
  width: 100%;
  height: auto;
}
.env-stamp::before {
  /* perforated edge effect */
  content: "";
  position: absolute;
  inset: -2px;
  background:
    radial-gradient(circle at 50% 0,   var(--cream) 2px, transparent 2.5px) repeat-x top    / 6px 4px,
    radial-gradient(circle at 50% 100%, var(--cream) 2px, transparent 2.5px) repeat-x bottom / 6px 4px,
    radial-gradient(circle at 0  50%, var(--cream) 2px, transparent 2.5px) repeat-y left   / 4px 6px,
    radial-gradient(circle at 100% 50%, var(--cream) 2px, transparent 2.5px) repeat-y right  / 4px 6px;
  pointer-events: none;
}
.env-postmark {
  position: absolute;
  top: 18%;
  right: -34%;
  width: 70px;
  pointer-events: none;
  transform: rotate(-12deg);
}
.env-postmark svg { width: 100%; height: auto; }

.env-to {
  position: absolute;
  left: 50%;
  top: 56%;
  transform: translate(-50%, -50%);
  text-align: center;
  z-index: 2;
  width: 80%;
}
.env-to-label {
  font-family: var(--font-body);
  font-size: clamp(9px, 1.05vw, 10.5px);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(61,47,36,0.45);
  margin-bottom: 6px;
}
.env-to-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(42px, 8.5vw, 76px);
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.01em;
  /* a soft ink-bleed feel */
  text-shadow: 0 1px 0 rgba(255,255,255,0.35);
}
.env-to-sub {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(11px, 1.4vw, 14px);
  color: rgba(61,47,36,0.55);
  margin-top: 6px;
}
.env-to::before, .env-to::after {
  content: "";
  display: block;
  width: 36%;
  height: 1px;
  background: rgba(61,47,36,0.18);
  margin: 0 auto;
}
.env-to::before { margin-bottom: 10px; }
.env-to::after  { margin-top: 12px; }

/* 4-layer envelope back. Z-order (front → back):
     z=5  seal       (on flap, fades with it)
     z=4  flap       (top closure; rotates open)
     z=3  body       (back panel; covers bottom of envelope so card hides behind it)
     z=2  paper      (card; full size; slides UP through layers when emerging)
     z=1  liner      (inside-front of envelope; visible when flap is open)
   The env-back clip lets the card extend ABOVE the envelope while hiding any
   part of it that would otherwise stick out BELOW the envelope. */
.env-back {
  clip-path: inset(-2000px -2000px 0 -2000px);
}

/* z=1 — Liner: inside surface of the front panel. Slightly lighter cream */
.env-liner {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: linear-gradient(180deg, #f0d9ce 0%, #e6c2b3 100%);
  z-index: 1;
}

.env-body {
  /* z=3 — Back panel. Covers the BOTTOM portion of the envelope so it
     hides the card while inside. Uses `inset: 0` + clip-path so its
     gradient is positionally IDENTICAL to the liner's — they share the
     same coordinate system, so there's no visible seam where they meet. */
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, #f0d9ce 0%, #e6c2b3 100%);
  border-radius: 4px;
  z-index: 3;
  /* Default height: bottom 55% of envelope (top 45% clipped away). */
  clip-path: inset(45% 0 0 0 round 0 0 4px 4px);
  transition: clip-path 0.6s cubic-bezier(.22,.65,.25,1);
}

/* When the envelope opens, the back panel rises higher so more of the
   envelope reads as the back side — and the card slides UP within it so
   a white sliver shows above the body's top edge. */
.envelope.phase-opening .env-body,
.envelope.phase-peeking .env-body,
.envelope.phase-out     .env-body,
.envelope.phase-focused .env-body {
  clip-path: inset(32% 0 0 0 round 0 0 4px 4px);
}

.env-flap {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 60%;
  transform-origin: top center;
  /* Soft ease-in-out so the flap opens with a sense of weight rather than
     snapping. Synced with the JS timer (1100ms) that triggers peeking. */
  transition: transform 1.1s var(--ease-soft);
  z-index: 4;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,0.08));
}
.env-flap svg { display: block; width: 100%; height: 100%; }
.envelope.opening .env-flap {
  transform: rotateX(-178deg);
  /* Drop the flap BEHIND the card once it has cleared the body region.
     Physically, an open flap is no longer in front of anything coming
     out of the envelope. Deferred to the midpoint of the rotation via
     a keyframe so the closed-flap silhouette doesn't break at frame 1. */
  animation: flap-z-drop 1.1s forwards;
}
@keyframes flap-z-drop {
  0%, 45%  { z-index: 4; }
  46%, 100% { z-index: 1; }
}

.env-letter {
  display: none;
}

.env-seal {
  position: absolute;
  /* sits where the flap's bottom point meets the body */
  left: 50%; top: 60%;
  transform: translate(-50%, -50%) rotate(-5deg);
  width: 78px; height: 78px;
  z-index: 5;
  transition: opacity 0.3s, transform 0.3s;
  pointer-events: none;
}
.env-seal svg { display: block; width: 100%; height: 100%; }
.envelope.opening .env-seal {
  opacity: 0;
  transform: translate(-50%, -50%) rotate(-22deg) scale(0.7);
}
@media (max-width: 540px) {
  .env-seal { width: 60px; height: 60px; }
}

.env-letter h2 {
  font-size: clamp(20px, 3.2vw, 30px);
  margin: 0 0 6px;
  font-style: italic;
}
.env-letter p {
  margin: 0;
  font-size: clamp(11px, 1.4vw, 13px);
  color: var(--ink-soft);
  line-height: 1.5;
}

.tap-hint {
  margin-top: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-soft);
  animation: pulse 2.2s ease-in-out infinite;
  pointer-events: none;
}
.tap-hint .arrow {
  display: inline-block;
  font-size: 20px;
  font-style: normal;
  line-height: 1;
  color: var(--blush-deep);
}
.tap-hint .arrow.h-left  { animation: nudge-left  1.6s ease-in-out infinite; }
.tap-hint .arrow.h-right { animation: nudge-right 1.6s ease-in-out infinite; }
.tap-hint .arrow.v-up    { animation: nudge-up    1.3s ease-in-out infinite; }

@keyframes nudge-left {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(-6px); }
}
@keyframes nudge-right {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(6px); }
}
@keyframes nudge-up {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}

/* Hearts floating up out of envelope */
.heart-burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
}
.heart-burst .h {
  position: absolute;
  left: 50%;
  bottom: 40%;
  width: 22px; height: 22px;
  opacity: 0;
  transform: translate(-50%, 0) scale(0.4);
  animation: heart-rise 2.6s ease-out forwards;
}
@keyframes heart-rise {
  0%   { opacity: 0; transform: translate(-50%, 0) scale(0.3) rotate(var(--rot,0deg)); }
  18%  { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx,0px)), var(--dy,-260px)) scale(var(--sc,1)) rotate(var(--rot,0deg)); }
}

/* ── Letter card (z=2) lives INSIDE the envelope back and emerges by
   translating upward. Stays full size at all times — NO scaling deformation.
   Visibility is provided naturally by the body (z=3) which hides any part
   of the card still inside the envelope. */
.env-paper {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 96%;
  max-width: 404px;
  z-index: 2;
  /* Default: card tucked deep inside the envelope, hidden by body+liner */
  transform: translate(-50%, 24%);
  transform-origin: center center;
  opacity: 0;
  pointer-events: none;
  background: #ffffff;
  border-radius: 2px;
  padding: 60px 60px 56px;
  text-align: left;
  color: var(--ink);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.6),
    0 12px 28px rgba(61,47,36,0.18),
    0 30px 60px rgba(61,47,36,0.08);
  transition:
    transform 0.95s var(--ease-tactile),
    opacity 0.5s var(--ease-tactile),
    box-shadow 0.55s var(--ease-tactile);
}

/* Card emerges CONCURRENTLY with the flap rotation rather than after it.
   By the time the flap is ~halfway open the card is already at the peek
   position. Slightly faster transition than the default so the reveal
   is snappy. The .peeking class below holds the same end-state. */
.envelope.phase-opening .env-paper {
  opacity: 1;
  transform: translate(-50%, -17%);
  transition:
    transform 0.65s var(--ease-tactile),
    opacity 0.35s var(--ease-tactile);
}

/* Peeking: card sits with a thin sliver of its top edge above the body's
   top edge. z-index stays at default (2) — BELOW the body (z=3) — so the
   body naturally hides the bottom of the card. The flap, once open, has
   dropped to z=1 via the keyframe animation above, so the card is in
   FRONT of the flap (physically correct for a card emerging up and out). */
.env-paper.peeking {
  opacity: 1;
  transform: translate(-50%, -17%);
}

/* Out: card has slid up out of the envelope.
   NOTE: position:fixed does NOT anchor to the viewport when an ancestor has
   a transform or perspective — and .envelope-wrap has both. So we stay on
   position:absolute and compensate for the wrap's translateY(45vh) inside
   the transform itself. translate(-50%, calc(-50% - 45vh)) puts the card's
   center at viewport center, undoing the wrap's downward shift.
   Z-index stays low through the slide so the body naturally hides any
   part of the card still inside the envelope as it rises. */
.env-paper.out {
  opacity: 1;
  transform: translate(-50%, calc(-50% - 45vh)) rotate(-1.2deg);
  pointer-events: auto;
}

/* Focused: dim the SURROUND, keep the card bright. Same position + size
   as `out`, but elevated above everything and with a gentle transform-based
   zoom (1.06×) that preserves aspect ratio. CRITICAL: do NOT change
   width/max-width here vs the .out / base rules — any layout-size change
   would deform the card during the transition. The zoom is purely via
   transform so the paper's proportions stay locked. */
.env-paper.focused {
  z-index: 10000;
  transform: translate(-50%, calc(-50% - 45vh)) scale(1.06) rotate(0deg);
  opacity: 1;
  background: #ffffff;
  /* The 100vmax outer ring paints the dim AROUND the card (not on it).
     The inset highlight at top + the 1px white outline give the card a
     bright, crisp edge that pops clearly off the dim surround. */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.95),
    0 0 0 1px rgba(255,255,255,0.7),
    0 28px 64px rgba(0,0,0,0.58),
    0 0 0 100vmax rgba(20, 14, 10, 0.74);
  transition:
    transform 0.7s var(--ease-tactile),
    box-shadow 0.7s var(--ease-tactile),
    opacity 0.5s var(--ease-tactile);
}

/* During focused, release env-back's clip-path so the card's 100vmax dim
   shadow can extend all the way across the viewport unclipped. */
.envelope.phase-focused .env-back {
  clip-path: none;
}

/* Fold crease styles removed — paper now reads cleaner without them */
.paper-body {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
}

/* ── Letter typography — dark, legible, all-serif. The card is on a
   pure-white background that sits inside a heavy dim once focused, so
   the text needs strong contrast (--ink) to read clearly. */
.paper-greeting {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 30px;
  margin: 0 0 28px;
  color: var(--ink);
  line-height: 1.2;
  letter-spacing: -0.005em;
}

.paper-p {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 18px;
  line-height: 1.6;
  color: var(--ink);
  margin: 0 0 24px;
  text-align: left;
}
.paper-p em {
  font-style: italic;
  color: var(--blush-deep);
  font-weight: 500;
}

.paper-line {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 19px;
  color: var(--ink-soft);
  margin: 18px 0 24px;
  text-align: center;
  letter-spacing: 0.005em;
}

.paper-signoff {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  color: var(--ink-soft);
  margin: 44px 0 0;
  text-align: right;
  line-height: 1.2;
}
.paper-sig {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 30px;
  color: var(--ink);
  margin: 14px 0 0;
  text-align: right;
  line-height: 1;
  letter-spacing: -0.01em;
}

.paper-cta {
  align-self: center;
  margin-top: 52px;
  padding: 14px 28px;
  font-size: 12px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.5s 0.2s ease, transform 0.5s 0.2s ease;
}
.env-paper.out .paper-cta {
  opacity: 1;
  transform: translateY(0);
}

@media (max-width: 540px) {
  /* Width/max-width here are inherited from .env-paper across ALL phases so
     the card never changes layout size between peek → out → focused. Any
     "zoom" on focused is purely transform-scaled and so preserves aspect
     ratio (no awkward in-between resize). */
  .env-paper { padding: 44px 36px 44px; width: 94%; max-width: 340px; }
  .env-paper.peeking { transform: translate(-50%, -17%); }
  .env-paper.out     { transform: translate(-50%, calc(-50% - 45vh)) rotate(-1deg); }
  .env-paper.focused { transform: translate(-50%, calc(-50% - 45vh)) scale(1.06) rotate(0deg); }
  .paper-greeting { font-size: 24px; margin-bottom: 20px; }
  .paper-p { font-size: 15px; line-height: 1.55; margin-bottom: 18px; }
  .paper-sig { font-size: 26px; margin-top: 10px; }
  .paper-signoff { margin-top: 32px; }
  .paper-cta { margin-top: 40px; }
}

/* Short-viewport adjustments — ensure card fits. Smaller viewports need a
   bigger vh shift on the wrap to give the card enough clearance above the
   envelope (the card is fixed-size-ish while the envelope scales with vh). */
@media (max-height: 720px) {
  .env-paper { padding: 36px 40px 36px; }
  .paper-greeting { font-size: 22px; margin-bottom: 16px; }
  .paper-p { font-size: 15px; line-height: 1.55; margin-bottom: 14px; }
  .paper-signoff { margin-top: 24px; font-size: 14px; }
  .paper-sig { font-size: 24px; margin-top: 10px; }
  .paper-cta { margin-top: 32px; padding: 10px 22px; }
  .intro-p-out .envelope-wrap,
  .intro-p-focused .envelope-wrap { transform: translateY(50vh); }
  .env-paper.out     { transform: translate(-50%, calc(-50% - 50vh)) rotate(-1.2deg); }
  .env-paper.focused { transform: translate(-50%, calc(-50% - 50vh)) scale(1.04) rotate(0deg); }
}

/* ── Buttons ──────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 26px;
  border: none;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 999px;
  transition:
    transform 0.22s var(--ease-snap),
    box-shadow 0.28s var(--ease-tactile),
    background 0.22s var(--ease-tactile);
  box-shadow: 0 8px 22px rgba(61,47,36,0.18);
}
.btn:hover {
  transform: translateY(-1px);
  background: #2d2218;
  box-shadow: 0 14px 30px rgba(61,47,36,0.24);
}
.btn:active { transform: translateY(0); transition-duration: 0.08s; }
.btn.ghost {
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  box-shadow: none;
}
.btn.ghost:hover { background: rgba(61,47,36,0.04); color: var(--ink); }

/* ── Choose screen ────────────────────────────────────── */
.choose {
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 40px;
  opacity: 0;
  animation: rise-in 0.7s 0.15s forwards;
}

.choose-head {
  text-align: center;
  max-width: 720px;
  margin: 0 auto;
}
.choose-head .eyebrow { margin-bottom: 14px; }
.choose-head h2 {
  font-size: clamp(30px, 5vw, 48px);
  margin: 0 0 12px;
  line-height: 1.1;
}
.choose-head h2 em { color: var(--blush-deep); }
.choose-head p {
  margin: 0 auto;
  max-width: 540px;
  color: var(--ink);
  line-height: 1.6;
  font-size: 15px;
}
.voucher-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px 6px 8px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink);
  margin-top: 16px;
}
.voucher-chip .dot {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--gold);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  color: white;
  font-size: 13px;
}
.voucher-chip b { color: var(--ink); font-weight: 600; }

/* ── Floating orbs (charity picker) ──────────────────────
   Pokeball-style spheres for each charity. Tap to open the detail card. */
.orbs {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(28px, 5vw, 60px);
  margin: 24px auto 12px;
  flex-wrap: wrap;
}

.orb {
  --orb-size: clamp(120px, 16vw, 170px);
  --tint: var(--c-bbrf);
  --tint-deep: var(--c-bbrf-deep);
  position: relative;
  width: var(--orb-size);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
  /* Springy transition: slight overshoot for "real object settling" feel
     on both selection and de-selection. */
  transition:
    transform 0.7s cubic-bezier(0.34, 1.56, 0.64, 1),
    opacity 0.55s var(--ease-tactile),
    filter 0.55s var(--ease-tactile);
}

/* Subdued — shrink + slide down to make room for the detail card.
   Stagger by orb index (--orb-delay is 0/0.6/1.2 from React; multiply
   way down so the cascade is 0/48/96ms — perceptible but not slow). */
.orb.subdued {
  transform: scale(0.6) translateY(40px);
  opacity: 0.7;
  filter: saturate(0.85);
  transition-delay: calc(var(--orb-delay, 0s) * 0.08);
  z-index: 1;
}
.orb.subdued .orb-tag { opacity: 0.6; }

/* Selected — settle to 1.06×, plus a one-shot press-bounce keyframe
   that fires when the .selected class is added. The bounce reads as
   "I just got picked" feedback. */
.orb.selected {
  transform: scale(1.06);
  z-index: 2;
  animation: orb-press 0.65s cubic-bezier(0.34, 1.56, 0.64, 1) 1;
}
@keyframes orb-press {
  0%   { transform: scale(1); }
  18%  { transform: scale(0.9); }     /* press down */
  55%  { transform: scale(1.16); }    /* overshoot */
  100% { transform: scale(1.06); }    /* settle */
}

/* :active — give the orb a brief press-down while the finger is on it.
   Pause the float so the transform is observable; resume on release. */
.orb:active:not(.disabled) .orb-body {
  transform: scale(0.93);
  animation-play-state: paused;
  transition: transform 0.08s ease-out;
}
.orb[data-c="bbrf"] { --tint: var(--c-bbrf); --tint-deep: var(--c-bbrf-deep); }
.orb[data-c="kab"]  { --tint: var(--c-kab);  --tint-deep: var(--c-kab-deep);  }
.orb[data-c="dwb"]  { --tint: var(--c-dwb);  --tint-deep: var(--c-dwb-deep);  }

/* The animated body — a soft single-color orb with a glossy highlight,
   bobbing in place. The charity's SVG crest sits centered inside. */
.orb-body {
  position: relative;
  width: var(--orb-size);
  height: var(--orb-size);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle at 32% 28%, rgba(255,255,255,0.55), transparent 55%),
    radial-gradient(circle at 65% 75%, rgba(0,0,0,0.18), transparent 55%),
    radial-gradient(circle at 50% 50%, var(--tint) 0%, var(--tint-deep) 100%);
  box-shadow:
    inset -8px -12px 28px rgba(0, 0, 0, 0.18),
    inset 6px 6px 14px rgba(255, 255, 255, 0.35),
    0 18px 28px rgba(61, 47, 36, 0.22),
    0 4px 8px rgba(61, 47, 36, 0.1);
  animation: orb-float 5.2s ease-in-out infinite;
  animation-delay: var(--orb-delay, 0s);
  transition:
    transform 0.32s var(--ease-tactile),
    box-shadow 0.55s var(--ease-tactile);
}

/* Gentle glow halo on the selected orb. A two-layer animated drop-shadow
   pulses outward in the charity's tint so "chosen" reads at a glance. */
.orb.selected .orb-body {
  animation:
    orb-float 5.2s ease-in-out infinite,
    orb-glow 2.6s ease-in-out infinite;
  animation-delay: var(--orb-delay, 0s), 0s;
}
@keyframes orb-glow {
  0%, 100% {
    box-shadow:
      inset -8px -12px 28px rgba(0, 0, 0, 0.18),
      inset 6px 6px 14px rgba(255, 255, 255, 0.35),
      0 0 16px color-mix(in oklab, var(--tint) 55%, transparent),
      0 18px 28px rgba(61, 47, 36, 0.22);
  }
  50% {
    box-shadow:
      inset -8px -12px 28px rgba(0, 0, 0, 0.18),
      inset 6px 6px 14px rgba(255, 255, 255, 0.35),
      0 0 36px color-mix(in oklab, var(--tint) 80%, transparent),
      0 22px 34px rgba(61, 47, 36, 0.26);
  }
}

/* SVG crest centered inside the orb, in white so it reads on the tint. */
.orb-crest {
  position: relative;
  z-index: 2;
  color: white;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.22));
  pointer-events: none;
}
.orb-crest svg {
  width: calc(var(--orb-size) * 0.48);
  height: calc(var(--orb-size) * 0.48);
}
.orb:hover:not(.disabled) .orb-body {
  animation-play-state: paused;
  transform: translateY(-6px) scale(1.04);
  box-shadow:
    inset -10px -14px 30px rgba(0, 0, 0, 0.22),
    inset 6px 6px 14px rgba(255, 255, 255, 0.35),
    0 26px 40px rgba(61, 47, 36, 0.28),
    0 6px 10px rgba(61, 47, 36, 0.12);
}
/* :active press-down is defined above in the .orb states block. */

@keyframes orb-float {
  0%, 100% { transform: translateY(0) rotate(-1.2deg); }
  50%      { transform: translateY(-12px) rotate(1.2deg); }
}

/* Shadow ellipse beneath each orb that responds to the float. */
.orb-shadow {
  position: absolute;
  left: 14%;
  right: 14%;
  top: calc(var(--orb-size) - 8px);
  height: 14px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(61,47,36,0.32), transparent 70%);
  filter: blur(6px);
  animation: orb-shadow-pulse 5.2s ease-in-out infinite;
  animation-delay: var(--orb-delay, 0s);
  z-index: -1;
  pointer-events: none;
}
@keyframes orb-shadow-pulse {
  0%, 100% { transform: scaleX(1) scaleY(1); opacity: 0.85; }
  50%      { transform: scaleX(0.82) scaleY(0.7); opacity: 0.55; }
}

.orb-tag {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: center;
  white-space: nowrap;
  transition:
    color 0.45s var(--ease-tactile),
    transform 0.45s var(--ease-tactile),
    opacity 0.45s var(--ease-tactile);
}

/* When the orb is chosen, its tag deepens to the charity tint + lifts
   slightly — a small typographic cue that says "this one is active". */
.orb.selected .orb-tag {
  color: var(--tint-deep);
  font-weight: 600;
  transform: translateY(-2px);
}

.orb.disabled { opacity: 0.55; cursor: not-allowed; }

.orbs-hint {
  text-align: center;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-mute);
  margin-top: 24px;
}

@media (max-width: 640px) {
  .orbs { gap: 14px; margin-top: 10px; }
  .orb { --orb-size: clamp(82px, 26vw, 110px); gap: 14px; }
  .orb-tag { font-size: 9px; letter-spacing: 0.16em; }
}

/* Inline charity detail card. Slides up from behind the orbs with a
   springy overshoot, choreographed to land AFTER the orbs have settled
   into their selected/subdued positions. */
.charity-detail {
  width: 100%;
  max-width: 480px;
  margin: 26px auto 0;
  --tint: var(--c-bbrf-deep);
  opacity: 0;
  animation: detail-rise-in 0.7s 0.18s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  /* Keep the swipe gesture from selecting card text. */
  user-select: none;
  -webkit-user-select: none;
}
.charity-detail[data-c="bbrf"] { --tint: var(--c-bbrf-deep); }
.charity-detail[data-c="kab"]  { --tint: var(--c-kab-deep);  }
.charity-detail[data-c="dwb"]  { --tint: var(--c-dwb-deep);  }

@keyframes detail-rise-in {
  0%   { opacity: 0; transform: translateY(48px) scale(0.92); }
  35%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

.detail-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 22px 26px 22px;
  text-align: center;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.7) inset,
    0 14px 32px rgba(61,47,36,0.08);
  will-change: transform;
}

.detail-tag {
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--tint);
  font-weight: 500;
  margin-bottom: 6px;
}
.detail-name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  line-height: 1.18;
  letter-spacing: -0.005em;
  margin: 0 0 10px;
  color: var(--ink);
}
.detail-blurb {
  font-family: var(--font-display);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  margin: 0;
  max-width: 380px;
  margin-left: auto;
  margin-right: auto;
}
.detail-web {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12.5px;
  color: var(--ink-soft);
  margin: 8px 0 0;
}
.detail-cta {
  margin-top: 16px;
  padding: 13px 22px;
  font-size: 12px;
  background: var(--tint);
}
.detail-cta:hover {
  background: var(--tint);
  filter: brightness(0.93);
}
.detail-cta:disabled { opacity: 0.5; cursor: not-allowed; }

@media (max-width: 640px) {
  .charity-detail { margin-top: 18px; }
  .detail-card { padding: 18px 18px 18px; }
  .detail-name { font-size: 19px; }
  .detail-blurb { font-size: 14px; }
}

/* (legacy .cards grid still exists below but is unused — left for any
   future fallback layout, e.g. a "no-orb" tweak option) */
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
/* Keep three columns much further down — only collapse to a single column
   on narrow mobile so the wedding viewer (typically tablet/laptop) always
   sees all three causes side by side without scrolling. */
@media (max-width: 640px) {
  .cards { grid-template-columns: 1fr; gap: 14px; }
}

.card {
  --tint: var(--c-bbrf);
  --tint-deep: var(--c-bbrf-deep);
  position: relative;
  background: var(--paper);
  border-radius: 18px;
  padding: 24px 22px 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  border: 1px solid var(--line);
  cursor: pointer;
  overflow: hidden;
  transition: transform 0.3s cubic-bezier(.2,.7,.2,1), box-shadow 0.3s, border-color 0.3s;
  box-shadow: 0 1px 0 rgba(255,255,255,0.7) inset, 0 10px 30px rgba(61,47,36,0.06);
}
.card:hover {
  transform: translateY(-4px);
  border-color: var(--tint);
  box-shadow: 0 1px 0 rgba(255,255,255,0.7) inset, 0 18px 40px rgba(61,47,36,0.12);
}
.card .ribbon {
  position: absolute;
  inset: 0 0 auto 0;
  height: 64px;
  background:
    linear-gradient(180deg, color-mix(in oklab, var(--tint) 38%, transparent) 0%, transparent 100%);
  pointer-events: none;
}
.card .crest {
  position: relative;
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--tint);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 4px;
  color: white;
  box-shadow: inset 0 -3px 8px rgba(0,0,0,0.10), 0 4px 10px color-mix(in oklab, var(--tint) 40%, transparent);
}
.card .crest svg { width: 28px; height: 28px; }

.card .tag {
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--tint-deep);
  font-weight: 500;
}
.card h3 {
  font-size: 21px;
  margin: 0;
  line-height: 1.18;
  letter-spacing: -0.005em;
}
.card .blurb {
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink);
  margin: 0;
  flex: 1;
}

.card .card-web {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12px;
  color: var(--ink-soft);
  margin-top: 4px;
}

.card .pick {
  margin-top: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 14px;
  border-radius: 999px;
  background: var(--tint);
  color: white;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: background 0.2s, transform 0.2s;
}
.card:hover .pick { background: var(--tint-deep); }
.card.disabled { cursor: not-allowed; opacity: 0.55; }
.card.disabled:hover { transform: none; }

/* Mini heart on each card that flies on click */
.card .heart-seed {
  position: absolute;
  top: 22px; right: 22px;
  width: 26px; height: 26px;
  color: var(--tint-deep);
  opacity: 0.85;
  transition: transform 0.3s;
}
.card:hover .heart-seed { transform: scale(1.1) rotate(-6deg); }

/* tints per card */
.card[data-c="bbrf"] { --tint: var(--c-bbrf); --tint-deep: var(--c-bbrf-deep); }
.card[data-c="kab"]  { --tint: var(--c-kab);  --tint-deep: var(--c-kab-deep);  }
.card[data-c="dwb"]  { --tint: var(--c-dwb);  --tint-deep: var(--c-dwb-deep);  }

/* ── Donating scene ───────────────────────────────────── */
.donating {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  text-align: center;
  opacity: 0;
  /* Longer, more deliberate fade-in so the transition from "I just chose"
     to "watch your gift land" feels slow and intentional. */
  animation: rise-in 1.4s 0.25s forwards;
}

/* The charity name is the only text on this page — sit prominently above
   the jar in italic serif. */
.donating-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(26px, 4.4vw, 38px);
  color: var(--ink);
  margin: 0;
  line-height: 1.15;
  letter-spacing: -0.005em;
  max-width: 480px;
}
.donating .eyebrow { color: var(--ink-soft); }
.donating h2 {
  font-size: clamp(28px, 4.4vw, 42px);
  margin: 0;
  line-height: 1.15;
}
.donating h2 em { font-style: italic; color: var(--blush-deep); }

.jar-stage {
  position: relative;
  width: 340px;
  height: 360px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.flying-heart {
  position: fixed;
  left: 0; top: 0;
  width: 36px; height: 36px;
  z-index: 1000;
  pointer-events: none;
  transition: transform 1.0s cubic-bezier(.55,.05,.3,1), opacity 0.4s;
  color: var(--blush-deep);
}

/* Coin that drops in */
.coin-falling {
  position: absolute;
  width: 36px; height: 36px;
  left: 50%;
  top: -40px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #ffd994, var(--gold) 55%, var(--gold-deep) 100%);
  box-shadow: 0 2px 6px rgba(163,127,61,0.4), inset 0 -2px 4px rgba(0,0,0,0.18);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-style: italic;
  color: white;
  font-weight: 500;
  font-size: 14px;
  /* Slower fall with later kickoff — heart morph runs longer before. */
  animation: coin-fall 1.6s 3.0s cubic-bezier(.4, .05, .25, 1) forwards;
  z-index: 5;
}
@keyframes coin-fall {
  0%   { top: -40px; transform: translateX(-50%) rotate(0deg); opacity: 0; }
  10%  { opacity: 1; }
  85%  { top: calc(100% - 86px); transform: translateX(-50%) rotate(280deg); }
  100% { top: calc(100% - 80px); transform: translateX(-50%) rotate(360deg); opacity: 0; }
}

/* Splash ripples when coin lands */
.splash {
  position: absolute;
  bottom: 50px;
  left: 50%;
  width: 80px; height: 16px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: rgba(201,162,91,0.5);
  opacity: 0;
  animation: splash 0.7s 4.4s ease-out forwards;
}
@keyframes splash {
  0%   { opacity: 0; transform: translateX(-50%) scale(0.4); }
  40%  { opacity: 0.6; }
  100% { opacity: 0; transform: translateX(-50%) scale(1.6); }
}

/* Heart burst — radiates outward from the jar mouth when the coin lands.
   Each heart rotates around a center then translates along its own
   rotated x-axis (rotate+translate+counter-rotate is the classic CSS
   radial-burst trick — heart faces upright while flying outward). */
.coin-hearts {
  position: absolute;
  left: 50%;
  bottom: 90px;
  width: 1px;
  height: 1px;
  pointer-events: none;
  z-index: 6;
}
.coin-heart {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  color: var(--blush-deep);
  animation: heart-fly 1.8s cubic-bezier(.22, 1, .36, 1) forwards;
  transform-origin: center;
}
.coin-heart.c0 { color: var(--blush-deep); }
.coin-heart.c1 { color: var(--blush); }
.coin-heart.c2 { color: var(--sage-deep); }
@keyframes heart-fly {
  0% {
    opacity: 0;
    transform: rotate(var(--angle, 0deg)) translate(0px) rotate(calc(-1 * var(--angle, 0deg))) scale(0.4);
  }
  20% {
    opacity: 1;
    transform: rotate(var(--angle, 0deg)) translate(28px) rotate(calc(-1 * var(--angle, 0deg))) scale(1.1);
  }
  100% {
    opacity: 0;
    transform: rotate(var(--angle, 0deg)) translate(140px) rotate(calc(-1 * var(--angle, 0deg))) scale(1);
  }
}

/* +$25 floats up above the jar with a springy bounce, then drifts away. */
.coin-plus25 {
  position: absolute;
  left: 50%;
  bottom: 110px;
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 38px;
  color: var(--gold-deep);
  opacity: 0;
  pointer-events: none;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
  animation: plus25-float 2.6s 4.45s cubic-bezier(.34, 1.56, .64, 1) forwards;
  z-index: 7;
}
@keyframes plus25-float {
  0%   { opacity: 0; transform: translate(-50%, 24px) scale(0.5); }
  18%  { opacity: 1; transform: translate(-50%, -6px) scale(1.18); }
  35%  { opacity: 1; transform: translate(-50%, -16px) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -130px) scale(1); }
}

/* ── Celebrate (between donating and thanks) ──────────── */
.celebrate {
  text-align: center;
  opacity: 0;
  /* Slow fade in, holds, then onComplete swaps to thanks (the new screen
     fades in via its own animation, giving us a fade-through). */
  animation: rise-in 1.6s 0.2s forwards;
}
.celebrate-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
.celebrate h1 {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(48px, 7vw, 88px);
  margin: 0;
  color: var(--ink);
  line-height: 1.05;
  letter-spacing: -0.01em;
}
.celebrate h1 em { font-style: italic; color: var(--blush-deep); }

/* ── Thanks / totals screen ───────────────────────────── */
.thanks {
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 40px;
  opacity: 0;
  animation: rise-in 1.0s 0.1s forwards;
}
.thanks-head h2 {
  font-size: clamp(34px, 5.4vw, 56px);
  margin: 0 0 14px;
  line-height: 1.1;
}
.thanks-head h2 em { color: var(--blush-deep); font-style: italic; }
.thanks-head p {
  color: var(--ink-soft);
  margin: 0 auto;
  max-width: 540px;
  line-height: 1.65;
  font-size: 16px;
}
/* On mobile, tighten head spacing + gap to claw back vertical height so
   the three tiles + actions all fit in one viewport. */
@media (max-width: 640px) {
  .thanks { gap: 22px; }
  .thanks-head h2 { font-size: 28px; margin-bottom: 10px; }
  .thanks-head p { font-size: 13.5px; line-height: 1.5; }
}
.thanks-head p .grand-inline {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1.15em;
  color: var(--ink);
}

/* Three horizontal-row tiles stacked vertically. The previous 3-column
   layout squeezed long charity names into narrow tiles and felt cramped;
   this layout gives each charity a full-width row with the crest + text
   on the left and the numbers on the right — breathing room without
   needing to scroll. */
.charity-summary {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 580px;
  margin: 0 auto;
  width: 100%;
}

.charity-tile {
  --tint: var(--c-bbrf);
  --tint-deep: var(--c-bbrf-deep);
  position: relative;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 20px 24px;
  display: flex;
  align-items: center;
  gap: 18px;
  text-align: left;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.7) inset,
    0 6px 18px rgba(61,47,36,0.05);
  transition:
    transform 0.4s var(--ease-tactile),
    box-shadow 0.4s var(--ease-tactile);
}
.charity-tile[data-c="bbrf"] { --tint: var(--c-bbrf); --tint-deep: var(--c-bbrf-deep); }
.charity-tile[data-c="kab"]  { --tint: var(--c-kab);  --tint-deep: var(--c-kab-deep);  }
.charity-tile[data-c="dwb"]  { --tint: var(--c-dwb);  --tint-deep: var(--c-dwb-deep);  }

.charity-tile.you {
  border-color: color-mix(in oklab, var(--tint) 60%, var(--line));
  background:
    linear-gradient(180deg,
      color-mix(in oklab, var(--tint) 14%, var(--paper)) 0%,
      var(--paper) 60%);
  box-shadow:
    0 1px 0 rgba(255,255,255,0.7) inset,
    0 10px 28px color-mix(in oklab, var(--tint) 20%, transparent);
}

.charity-tile .tile-crest {
  width: 52px; height: 52px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--tint);
  display: flex; align-items: center; justify-content: center;
  color: white;
  box-shadow:
    inset 0 -3px 8px rgba(0,0,0,0.10),
    0 4px 10px color-mix(in oklab, var(--tint) 35%, transparent);
}
.charity-tile .tile-crest svg { width: 26px; height: 26px; }

.charity-tile .tile-text {
  flex: 1;
  min-width: 0;
}

.charity-tile .tile-tag {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--tint-deep);
  font-weight: 500;
  margin-bottom: 4px;
}
.charity-tile .tile-name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 19px;
  line-height: 1.22;
  color: var(--ink);
  letter-spacing: -0.005em;
}

.charity-tile .tile-numbers {
  text-align: right;
  flex-shrink: 0;
  margin-left: 8px;
}
.charity-tile .tile-amt {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 26px;
  color: var(--ink);
  line-height: 1;
  letter-spacing: -0.01em;
}
.charity-tile .tile-donors {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12.5px;
  color: var(--ink-soft);
  margin-top: 5px;
  white-space: nowrap;
}

.charity-tile .tile-badge {
  display: inline-block;
  margin-top: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--blush);
  color: white;
  font-family: var(--font-body);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

@media (max-width: 640px) {
  .charity-summary { max-width: none; }
  .charity-tile { padding: 14px 16px; gap: 14px; border-radius: 16px; }
  .charity-tile .tile-crest { width: 44px; height: 44px; }
  .charity-tile .tile-crest svg { width: 22px; height: 22px; }
  .charity-tile .tile-tag { font-size: 9px; letter-spacing: 0.18em; margin-bottom: 3px; }
  .charity-tile .tile-name { font-size: 16px; line-height: 1.2; }
  .charity-tile .tile-amt { font-size: 22px; }
  .charity-tile .tile-donors { font-size: 11.5px; margin-top: 3px; }
  .charity-tile .tile-badge { font-size: 8.5px; padding: 2px 8px; margin-top: 5px; letter-spacing: 0.14em; }
}

.thanks-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/* ── Ambient petals ───────────────────────────────────── */
.ambient {
  position: fixed; inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
}
.ambient .petal {
  position: absolute;
  top: -40px;
  width: 16px; height: 22px;
  border-radius: 60% 40% 60% 40% / 70% 60% 40% 30%;
  background: linear-gradient(180deg, #f3d3cd 0%, #e2b9b1 100%);
  opacity: 0.45;
  animation: petal-fall linear infinite;
  transform-origin: center;
}
.ambient .petal:nth-child(odd) {
  background: linear-gradient(180deg, #cfd6c0 0%, #9eaa8c 100%);
  opacity: 0.32;
}
@keyframes petal-fall {
  0%   { transform: translateY(-20px) translateX(0) rotate(0deg); }
  100% { transform: translateY(110vh) translateX(var(--drift,40px)) rotate(540deg); }
}

/* keep main content above ambient */
.scene, .brand, .totals-mini { z-index: 2; }

/* ── Animations ───────────────────────────────────────── */
@keyframes fade-in { to { opacity: 1; } }
@keyframes rise-in {
  to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

/* Reduced motion friendliness */
@media (prefers-reduced-motion: reduce) {
  .ambient { display: none; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
