@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@700;800&display=swap');
/* The site-wide top nav (container, logo, nav-auth cluster) lives in one shared
   file so light and dark pages render an identical nav — see css/nav.css. */
@import url('css/nav.css');

.back-link-global { display: inline-flex !important; align-items: center !important; gap: 4px !important; color: var(--brand) !important; font-family: Inter, sans-serif !important; font-size: 14px !important; font-weight: 600 !important; text-decoration: none !important; padding: 0 !important; margin: 16px 0 24px 24px !important; float: none !important; position: relative !important; z-index: 1 !important; background: none !important; border: none !important; }
.back-link-global:hover { text-decoration: underline !important; }
.back-link-global .back-chevron { font-size: 18px !important; line-height: 1 !important; color: var(--brand) !important; }
.back-link-global .back-text { font-size: 14px !important; color: var(--brand) !important; font-family: Inter, sans-serif !important; }

:root {
  --primary: #7c3aed;
  --primary-dark: #5b21b6;
  --primary-light: #ede9fe;
  --wishd-gradient: linear-gradient(90deg, #5B21B6 0%, #7C3AED 50%, #5B21B6 100%);
  --accent: #f59e0b;
  --accent-dark: #d97706;
  --accent-tint: #FEF3C7;
  --success: #059669;
  --success-tint: #F0FDF4;
  --danger: #DC2626;
  --danger-dark: #B91C1C;
  --danger-tint: #FEF2F2;
  --text: #1e1b4b;
  --text-muted: #6b7280;
  --bg: #fafafa;
  --bg-card: #ffffff;
  --border: #e5e7eb;
  /* Consolidation phase 1: brand (was the site's most-used indigo literal,
     distinct from the violet --primary), its hover, and neutral surfaces. */
  --brand: #5B4FCF;
  --brand-dark: #4A3FB8;
  --surface: #ffffff;
  --surface-alt: #fafafa;
  --radius: 12px;
  --shadow: 0 4px 24px rgba(124,58,237,0.08);
  --shadow-lg: 0 8px 40px rgba(124,58,237,0.14);
  --paper-base: #FBF8F4;
  --paper-base-dark: #5B21B6;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  /* Sticky footer, site-wide: a flex column whose footer takes margin-top:auto
     sits at the viewport bottom when content is short and below content when
     long — no mid-screen float on short pages like the Gift Hub. Children stack
     exactly as in normal flow (align-items:stretch matches block full-width), so
     this is layout-neutral for existing pages. Pages that set their own
     body{display:flex} in a later <style> block keep theirs. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  line-height: 1.6;
}

/* NAV */
/* Transparent by design, matching index.html's .lp-nav (the reference) —
   nav is never sticky/fixed here, so it scrolls away with the page like any
   other content; there's no reason for it to paint over the body pattern. */
/* `nav`, `.nav-logo`, `.nav-dark` and the `.na-*` cluster now live in
   css/nav.css (imported at the top of this file). */
.nav-page-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-family: 'Poppins', sans-serif;
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--text);
  white-space: nowrap;
  pointer-events: none;
}

.nav-login {
  font-size: 0.85rem;
  font-weight: 600;
  font-family: 'Poppins', sans-serif;
  color: var(--brand);
  text-decoration: none;
  padding: 0.42rem 1rem;
  border: 1.5px solid var(--brand);
  border-radius: 50px;
  transition: background 0.15s, color 0.15s;
}
.nav-login:hover { background: var(--brand); color: var(--surface); }


/* HERO */
.hero {
  background: var(--wishd-gradient);
  color: white;
  text-align: center;
  padding: 5rem 2rem 4rem;
}

.hero h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  font-weight: 800;
  letter-spacing: -1px;
  margin-bottom: 1rem;
  line-height: 1.15;
}

.hero h1 span {
  color: #fde68a;
}

.hero p {
  font-size: 1.2rem;
  opacity: 0.9;
  max-width: 540px;
  margin: 0 auto 2.5rem;
}

/* CONTAINER */
/* width:100% is load-bearing. body is a flex column (sticky footer, above), and
   a flex item with auto horizontal margins does NOT stretch: the auto margins
   absorb the free space and the item shrink-wraps to its max-content width,
   capped by max-width. Without an explicit width every .container was sized by
   its widest child — the Gift Hub rendered 419px wide at 1280 (cap 640), and a
   nowrap card title or a nowrap button in a flex row pushed the container past
   the viewport on a phone (sideways scroll on the hub and settings at 320).
   It also made the hub's "Make a Wishd" button jump wider when the feed
   arrived, because the container grew around it. 100% gives auto margins
   nothing to absorb; max-width still caps; centring is unchanged. */
.container {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem;
}

.container-wide {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem;
}

/* CARD */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2rem;
  box-shadow: var(--shadow);
}

.card + .card {
  margin-top: 1.5rem;
}

/* Content panel — the landing FAQ / HOW IT WORKS box treatment, reused on the
   Our Story / Privacy / Terms pages so the cream doodle page shows around a
   var(--surface-alt) box in the same 980px column. Literal values mirror the landing
   boxes (no shadow — unlike .card above). */
.page-panel {
  max-width: 980px;
  margin: 0 auto;
  background: var(--surface-alt);
  border: 1px solid #ede9fe;
  border-radius: 18px;
  padding: 2.5rem;
}
@media (max-width: 640px) {
  .page-panel { padding: 1.5rem 1.25rem; }
}

/* Auth card + heading, shared by the auth pages. login/forgot/reset also carry
   local copies that predate this and override it identically; register uses
   these. Colours are literal (not var(--text)) so register's own local --text
   override can't repaint the heading near-black — it stays the brand indigo
   the login page shows. Fully responsive: width:100% down to the 420px cap, so
   the same treatment holds on mobile with no separate rule. */
.auth-card {
  background: var(--surface);
  border-radius: 16px;
  border: 1px solid var(--border);
  padding: 2.5rem 2rem;
  width: 100%;
  max-width: 420px;
  box-shadow: 0 4px 24px rgba(0,0,0,0.07);
}
.auth-title {
  font-size: 1.4rem;
  font-weight: 800;
  text-align: center;
  margin-bottom: 0.4rem;
  color: var(--text);
}

.card-title {
  font-size: 1.2rem;
  font-weight: 700;
  margin-bottom: 1.25rem;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Canonical card heading with a leading icon (the "Message to gifters" treatment).
   One class for every icon-led card heading across all flows, so they can't drift
   apart the way the old per-card inline copies did. Margin is intentionally left
   to the caller (0 by default; edit views add a bottom margin inline). */
.card-heading {
  font-family: 'Poppins', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.45rem;
}
.card-heading svg { width: 18px; height: 18px; flex-shrink: 0; }
/* Dashboard-scoped (.container-wide is dashboard.html only): one consistent
   heading-to-content gap on every card, so individual cards don't each set their
   own inline margin. Not on the bare .card-heading base rule, which is shared by
   the create wizard and /send. Cards that supply their own separation (Recipient
   notification's shaded box) keep an explicit inline margin:0 override. */
.container-wide .card-heading { margin: 0 0 0.75rem; }

/* FORM */
.form-group {
  margin-bottom: 1.25rem;
}

label {
  display: block;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 0.4rem;
}

input[type="text"],
input[type="email"],
input[type="number"],
input[type="password"],
input[type="date"],
select,
textarea {
  width: 100%;
  padding: 0.7rem 1rem;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 1rem;
  color: var(--text);
  background: white;
  transition: border-color 0.2s, box-shadow 0.2s;
  outline: none;
}

/* iOS Safari renders input[type=date] as a NATIVE control whose intrinsic
   width/height ignore `width:100%` and `box-sizing`, so on a narrow screen the
   field overflows its container. Desktop Chromium doesn't use those metrics
   (hence it barely reproduces there). `appearance:none` makes iOS lay the
   field out like an ordinary text input, respecting the shared rule above.
   The pseudo-elements neutralise the margins iOS gives the value text and the
   picker icon, which otherwise push the content past the box edge. font-size
   stays inherited at 1rem (16px) so tapping the field never triggers iOS zoom. */
input[type="date"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
}
input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
}
input[type="date"]::-webkit-calendar-picker-indicator {
  margin-left: auto;
  margin-right: 0;
  /* One shared calendar glyph so every flow's date field matches (create, /send,
     register, and the dashboard's inline-styled field — the pseudo-element is
     styled from here regardless of the element's inline styles). WebKit/Blink
     only: iOS Safari renders its own native date control and IGNORES this, so
     the custom icon appears on desktop and Android Chrome but not on iOS. */
  cursor: pointer;
  opacity: 1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%235B4FCF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
  background-size: 18px 18px;
  background-repeat: no-repeat;
  background-position: center;
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(124,58,237,0.12);
}

textarea {
  resize: vertical;
  min-height: 80px;
}

/* ── INLINE FIELD VALIDATION ── */
.field-wrap { position: relative; }
.field-wrap > input,
.field-wrap > select { padding-right: 2.5rem; }
.valid-tick {
  position: absolute; right: 0.75rem; top: 50%; transform: translateY(-50%);
  display: none; align-items: center; pointer-events: none;
}
.valid-tick.visible { display: flex; }
.field-error {
  display: none; align-items: center; gap: 5px;
  color: var(--danger); font-size: 13px; margin-top: 6px; line-height: 1.4;
}
.field-error.visible { display: flex; }
.field-error svg { flex-shrink: 0; }
.input-error { border-color: var(--danger) !important; box-shadow: 0 0 0 3px rgba(220,38,38,0.08) !important; }
.input-success { border-color: #16A34A !important; box-shadow: 0 0 0 3px rgba(22,163,74,0.08) !important; }

/* ── FILLED-INPUT TINT ── */
/* Purple tint on inputs/textareas once the user has typed something */
input[type="text"]:not(:placeholder-shown),
input[type="email"]:not(:placeholder-shown),
input[type="number"]:not(:placeholder-shown),
input[type="password"]:not(:placeholder-shown),
textarea:not(:placeholder-shown) {
  background-color: #f0eeff;
}
/* Purple tint on date inputs only once a date is actually picked */
input[type="date"]:not([value=""]) {
  background-color: #f0eeff;
}
/* Purple tint on selects once a real option is chosen (placeholder is option[value=""]) */
select:not(:has(option[value=""]:checked)) {
  background-color: #f0eeff;
}
/* Override browser autofill blue with the same purple tint */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px #f0eeff inset !important;
}

/* BUTTONS */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 14px 28px;
  border: none;
  border-radius: 50px;
  font-size: 0.85rem;
  font-weight: 600;
  font-family: 'Poppins', sans-serif;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s, background 0.2s;
  text-decoration: none;
}

.btn:active {
  transform: scale(0.98);
}

/* ── Button hierarchy ──────────────────────────────────────────────────────
   PRIMARY (.btn-primary): the page's main action — purple gradient fill,
   white text, hover darkens + lifts the shadow.
   SECONDARY (.btn-secondary / .btn-outline, same class): supporting actions
   — solid white fill, purple text/border; hover fills solid purple with
   white text (NOT a light tint — that was the old behaviour and is why
   different "Login"/"Logout" buttons across the app used to disagree with
   each other; every secondary button now hovers the same way).
   TERTIARY (.btn-text): low-emphasis actions (Skip, Back, Maybe later) —
   no fill or border ever, purple text, hover/focus underlines it.
   DESTRUCTIVE (.btn-destructive / .btn-danger): red fill, reserved for
   delete confirms.
   Every tier gets a focus-visible ring identical to its hover treatment —
   hover is never the only affordance, for keyboard users and (since there
   is no hover) touch devices, which always render the resting state. */
.btn-primary {
  background: var(--wishd-gradient);
  color: var(--surface);
  box-shadow: 0 2px 12px rgba(91,79,207,0.3);
}

.btn-primary:hover,
.btn-primary:focus-visible {
  background: var(--brand-dark);
  transform: translateY(-1px);
  box-shadow: 0 4px 20px rgba(91,79,207,0.4);
}

.btn-secondary,
.btn-outline {
  background: var(--surface);
  color: var(--brand);
  border: 1.5px solid var(--brand);
}

.btn-secondary:hover,
.btn-outline:hover,
.btn-secondary:focus-visible,
.btn-outline:focus-visible {
  background: var(--brand);
  color: var(--surface);
}

.btn-text {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: none;
  padding: 0;
  color: var(--brand);
  font-family: 'Inter', sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.btn-text:hover,
.btn-text:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.btn-destructive,
.btn-danger {
  background: var(--danger);
  color: var(--surface);
}

.btn-destructive:hover,
.btn-danger:hover,
.btn-destructive:focus-visible,
.btn-danger:focus-visible {
  background: var(--danger-dark);
}

.btn-accent {
  background: var(--accent);
  color: white;
  box-shadow: 0 2px 12px rgba(245,158,11,0.3);
}

.btn-accent:hover,
.btn-accent:focus-visible {
  background: var(--accent-dark);
}

.btn-full {
  width: 100%;
}

/* Lightweight destructive trigger (soft red) — opens a confirm modal rather
   than acting immediately, so it reads a step quieter than the solid-red
   .btn-destructive used on the confirm itself. Combine with .btn for the
   standard pill (delete a reel, remove an avatar); pages needing a smaller
   compact pill (e.g. "Remove from Gift Hub") can add their own font-size/
   padding on top — this class only owns colour + hover/focus. */
.btn-danger-soft {
  background: var(--danger-tint);
  color: var(--danger);
  border: 1.5px solid #fca5a5;
}
.btn-danger-soft:hover,
.btn-danger-soft:focus-visible {
  background: #fee2e2;
}
.btn-danger-soft:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 2px;
}

/* Compact 20px-radius chip variant of secondary — used for small inline
   actions attached to a heading (Edit Wishd, Release funds) rather than a
   full 50px pill. Same secondary hover semantics (solid fill, not a tint). */
.chip-btn {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: Inter, sans-serif; font-size: 0.8rem; font-weight: 600;
  /* Pin line-height so <a> and <button> chips match — otherwise the <a>
     inherits the page's 1.6 while a <button> uses UA line-height:normal,
     leaving them ~5px apart. 1.2 (not 1) keeps descenders off the border. */
  line-height: 1.2;
  text-decoration: none; padding: 3px 10px; border-radius: 20px;
  border: 1.5px solid; cursor: pointer; background: transparent;
  transition: background 0.15s, color 0.15s;
}
.chip-btn-secondary { color: var(--brand); border-color: var(--brand); }
.chip-btn-secondary:hover,
.chip-btn-secondary:focus-visible { background: var(--brand); color: var(--surface); }
.chip-btn-secondary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.chip-btn-affirm { color: var(--success); border-color: var(--success); }
.chip-btn-affirm:hover,
.chip-btn-affirm:focus-visible { background: var(--success); color: var(--surface); }
.chip-btn-affirm:focus-visible { outline: 2px solid var(--success); outline-offset: 2px; }
/* A chip that names a future state rather than offering an action (the target
   fund before T−30). Muted, no pointer, no hover fill — resting state only. */
.chip-btn-disabled,
.chip-btn-disabled:hover,
.chip-btn-disabled:focus-visible { color: var(--text-muted); border-color: var(--border); background: var(--surface); cursor: default; pointer-events: none; }

.btn:focus-visible,
.btn-text:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
/* .na-* focus-visible lives in css/nav.css alongside the cluster it belongs to. */

/* BADGE */
.badge {
  display: inline-block;
  padding: 0.2rem 0.7rem;
  border-radius: 100px;
  font-size: 0.78rem;
  font-weight: 600;
}

.badge-purple {
  background: var(--primary-light);
  color: var(--brand);
}

.badge-green {
  background: #d1fae5;
  color: #065f46;
}

.badge-yellow {
  background: var(--accent-tint);
  color: #92400e;
}

/* PROGRESS BAR */
.progress-wrap {
  margin: 1.25rem 0;
}

.progress-label {
  display: flex;
  justify-content: space-between;
  font-size: 0.88rem;
  color: var(--text-muted);
  margin-bottom: 0.4rem;
}

.progress-bar {
  height: 10px;
  background: var(--primary-light);
  border-radius: 100px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--primary), #818cf8);
  border-radius: 100px;
  transition: width 0.6s ease;
}

/* STATS */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1rem;
  margin-bottom: 2rem;
}

.stat-card {
  background: white;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  text-align: center;
  box-shadow: var(--shadow);
}

.stat-value {
  font-size: 2rem;
  font-weight: 800;
  color: var(--brand);
  line-height: 1;
}

.stat-label {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin-top: 0.3rem;
  font-weight: 500;
}

/* CONTRIBUTION LIST */
.contribution-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.contribution-item {
  background: white;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  box-shadow: var(--shadow);
}

.contributor-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--primary), #818cf8);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: 700;
  font-size: 1.1rem;
  flex-shrink: 0;
}

.contribution-info {
  flex: 1;
}

.contribution-info h4 {
  font-size: 0.98rem;
  font-weight: 700;
  margin-bottom: 0.2rem;
}

.contribution-info p {
  font-size: 0.88rem;
  color: var(--text-muted);
  font-style: italic;
}

.contribution-meta {
  text-align: right;
  flex-shrink: 0;
}

.contribution-amount {
  font-size: 1.1rem;
  font-weight: 800;
  color: var(--success);
}

.contribution-date {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.2rem;
}

/* VIDEO SECTION */
/* ── The one canonical reel box ─────────────────────────────────────────────
   9:16 + cover, width-driven so the ratio survives a narrow card. This is the
   SINGLE mechanism for every reel surface that imports style.css — it collapsed
   four that produced the same result (inline styles, the old .video-preview
   class, this, and gifter-view's local .gv-video-wrap). It works two ways:
     • directly on the media:  <video class="reel-frame">
     • as a wrapper:           <div class="reel-frame"><video></video></div>

   Two surfaces genuinely can't use it and keep their own copy:
     • reveal.html — doesn't import style.css (self-contained), so .reel-frame
       can't reach it; its #story-video restates the same 9:16 + cover, at a
       56.25vh full-screen cap instead of 39.375vh.
     • the recorder — #shared-reel-modal .video-preview lives in reel-modal.css
       because the modal is injected onto pages (reveal, demo) that have no
       style.css; it also has its own record/review box behaviour.

   WIDTH-driven, not height-driven: with an explicit height plus max-width:100%,
   a container narrower than the ratio demands clamps the width while the height
   stays put — and aspect-ratio can't override an explicit height — so the box
   silently stops being 9:16 (measured 0.516 in a narrow card). min() clamps the
   width by the container AND the height budget (39.375vh == 70vh x 9/16).

   cover, not contain: with a conforming 9:16 clip the two are identical; for a
   non-conforming one (a legacy reel, or a Safari fallback that skipped the
   recorder's canvas) cover crops where contain would letterbox. */
.reel-frame {
  /* 3:4 — the front camera's native portrait, so a phone-recorded reel fills
     this box with no crop (was 9:16, which cropped the 3:4 sensor ~25% and read
     as "too close"). The height cap follows the ratio: 52.5vh == 70vh x 3/4. */
  aspect-ratio: 3 / 4;
  width: min(100%, 52.5vh);
  height: auto;
  margin: 0 auto;
  background: #000;
  border-radius: 10px;
  overflow: hidden;
  display: block;
  object-fit: cover;   /* used when applied directly to a <video>; a no-op on a wrapping <div> */
}
.reel-frame > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  background: #000;
}

.video-controls {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.75rem;
  flex-wrap: wrap;
}

/* SHARE LINK */
.share-box {
  background: var(--primary-light);
  border: 1.5px dashed var(--primary);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.share-url {
  flex: 1;
  font-family: monospace;
  font-size: 0.9rem;
  color: var(--primary-dark);
  word-break: break-all;
}

/* ALERT / NOTICE */
.alert {
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  font-size: 0.92rem;
  margin-bottom: 1.25rem;
}

.alert-success {
  background: var(--success-tint);
  border: 1px solid #16A34A;
  color: #15803D;
}

.alert-error {
  background: var(--danger-tint);
  border: 1px solid var(--danger);
  color: var(--danger);
}

.alert-info {
  background: var(--primary-light);
  border: 1px solid #c4b5fd;
  color: var(--primary-dark);
}

.alert-warning {
  background: #fffbeb;
  border: 1px solid #fde68a;
  color: #92400e;
}

/* AMOUNT SELECTOR */
.amount-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.amount-btn {
  padding: 0.65rem;
  border: 2px solid var(--border);
  border-radius: 8px;
  background: white;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  color: var(--text);
  transition: all 0.15s;
  text-align: center;
}

.amount-btn:hover,
.amount-btn.selected {
  border-color: var(--primary);
  background: var(--primary-light);
  color: var(--brand);
}

/* SECTION HEADING */
.section-heading {
  font-size: 1.5rem;
  font-weight: 800;
  margin-bottom: 1.5rem;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* EMPTY STATE */
.empty-state {
  text-align: center;
  padding: 3rem 2rem;
  color: var(--text-muted);
}

.empty-state-icon {
  font-size: 3rem;
  margin-bottom: 0.75rem;
}

.empty-state h3 {
  font-size: 1.1rem;
  margin-bottom: 0.5rem;
  color: var(--text);
}

/* TABS */
.tabs {
  display: flex;
  border-bottom: 2px solid var(--border);
  margin-bottom: 1.5rem;
  gap: 0;
}

.tab {
  padding: 0.75rem 1.5rem;
  font-weight: 600;
  font-size: 0.92rem;
  cursor: pointer;
  border: none;
  background: none;
  color: var(--text-muted);
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  transition: all 0.2s;
}

.tab.active {
  color: var(--brand);
  border-bottom-color: var(--primary);
}

.tab-panel {
  display: none;
}

.tab-panel.active {
  display: block;
}

/* VIDEO MODAL */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  z-index: 999;
  align-items: center;
  justify-content: center;
  overflow-y: auto;
}

.modal-overlay.open {
  display: flex;
}

.modal {
  background: white;
  border-radius: var(--radius);
  padding: 2rem;
  max-width: 600px;
  width: 90%;
  position: relative;
  max-height: 90dvh;
  overflow-y: auto;
}

.modal-close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: var(--text-muted);
}
.modal-close:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Compact rectangular confirm/cancel button pair used inside modals (delete,
   release funds, cancel account, etc) — same 4-tier hierarchy as .btn, just
   an 8px-radius/Inter variant to match the modal's own compact aesthetic.
   .modal-btn-secondary is the "keep/cancel" side: it used to be reimplemented
   ad hoc per modal with a plain grey border (var(--border)) instead of
   purple — a real off-brand bug, now fixed by sharing one class everywhere. */
.modal-btn {
  border: none; border-radius: 8px; padding: 0.7rem 1.2rem;
  font-family: Inter, sans-serif; font-size: 0.88rem; font-weight: 600;
  cursor: pointer; white-space: nowrap; transition: background 0.15s, color 0.15s;
}
.modal-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.modal-btn-danger { background: var(--danger); color: var(--surface); }
.modal-btn-danger:hover, .modal-btn-danger:focus-visible { background: var(--danger-dark); }
.modal-btn-confirm { background: var(--success); color: var(--surface); }
.modal-btn-confirm:hover, .modal-btn-confirm:focus-visible { background: #047857; }
.modal-btn-secondary { background: var(--surface); color: var(--brand); border: 1.5px solid var(--brand); }
.modal-btn-secondary:hover, .modal-btn-secondary:focus-visible { background: var(--brand); color: var(--surface); }

/* FOOTER */
/* Sticky, site-wide: in the body flex column, margin-top:auto pushes the footer
   to the viewport bottom on short pages and collapses to 0 on long ones (its own
   padding still gives breathing room). Applies to any <footer> element. */
footer {
  margin-top: auto;
}

/* ── Shared site footer ──────────────────────────────────────────────────────
   ONE definition. The markup is public/partials/footer.html, injected server-
   side by sendHtmlPage() at each page's <!--WISHD_FOOTER--> marker. No inline
   styles, no !important — every colour is declared once, right here. Links use
   the brand purple; the small © line is one step deeper for legibility. */
.site-footer {
  text-align: center;
  padding: 0.9rem 1rem;
  font-size: 0.78rem;
  line-height: 1.45;
}
.site-footer-tagline { color: var(--text-muted); }
.site-footer-brand { color: var(--brand); font-weight: 600; }
.site-footer-links {
  display: flex; justify-content: center; align-items: center;
  flex-wrap: wrap; gap: 0.2rem 0.8rem; margin: 0.2rem 0;
  /* This is a <nav>, so it otherwise inherits the top bar's global
     `nav { height: 64px; padding: 0 2rem }` (css/nav.css) — a whole 64px tall
     for one line of links, which was most of the footer's wasted height. Reset it. */
  height: auto; padding: 0;
}
.site-footer-links a { color: var(--brand); text-decoration: none; }
.site-footer-links a:hover,
.site-footer-links a:focus-visible { text-decoration: underline; }
.site-footer-links span { color: #b9a9e8; }   /* · separators, muted purple */
.site-footer-copy { color: var(--brand-dark); font-size: 0.72rem; }

/* UTILS */
.text-center { text-align: center; }
.text-muted { color: var(--text-muted); }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }
.flex { display: flex; }
.flex-gap { display: flex; gap: 1rem; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.hidden { display: none; }

@media (max-width: 600px) {
  nav { padding: 0 1rem; }
  .hero { padding: 3rem 1rem 2.5rem; }
  .container, .container-wide { padding: 1.5rem 1rem; }
  .card { padding: 1.25rem; }
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
  .amount-grid { grid-template-columns: repeat(2, 1fr); }
  .share-box { flex-direction: column; }
}

/* ── Video choice buttons (Record vs Upload) ── */
.video-choice {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}
.vchoice-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  padding: 1.2rem 0.75rem;
  border: 2px solid var(--border);
  border-radius: 12px;
  background: white;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--text);
  transition: border-color 0.15s, background 0.15s, color 0.15s;
  line-height: 1.3;
  text-align: center;
}
.vchoice-btn:hover {
  border-color: var(--primary);
  background: var(--primary-light);
  color: var(--brand);
}
.vchoice-btn svg {
  width: 28px;
  height: 28px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}

/* ── Upload progress bar ── */
.upload-progress {
  display: none;
  margin-top: 0.75rem;
}
.upload-progress-bar {
  width: 100%;
  height: 8px;
  background: var(--border);
  border-radius: 4px;
  overflow: hidden;
}
.upload-progress-fill {
  height: 100%;
  background: var(--primary);
  border-radius: 4px;
  transition: width 0.1s linear;
  width: 0%;
}
.upload-progress-label {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 0.3rem;
  text-align: center;
}

/* ── Shared nav-right (auth-aware header) ── */
.nav-right { display:flex; align-items:center; gap:0.75rem; }
.nav-cta { font-size:0.82rem !important; padding:0.42rem 1rem !important; }
.nav-text-btn { font-family:'Inter',sans-serif; font-size:0.84rem; color:#666666; background:none; border:none; cursor:pointer; padding:0; text-decoration:none; }
.nav-text-btn:hover { color:#444444; }
.nav-icon-btn { display:flex; align-items:center; line-height:0; text-decoration:none; opacity:0.85; }
.nav-icon-btn:hover { opacity:1; }
.nav-login { font-size:0.84rem !important; padding:0.38rem 0.9rem !important; }

/* nav-auth's `.na-*` cluster and its ≤680/≤480 responsive steps live in
   css/nav.css (imported at the top of this file) — the single nav source. */

/* ── GIFTER OWN-SUMMARY CHIPS ──
   The "what you sent" row (money / reel / message), built once by
   wishd_gifterOwnSummaryChips() in shared-constants.js and rendered identically
   on every gifter surface. Compact purple pills, icon + label. */
.own-chip-row { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.own-chip {
  display: inline-flex; align-items: center; gap: 0.28rem;
  background: #f5f3ff; color: var(--brand); border-radius: 50px;
  padding: 0.16rem 0.55rem; font-family: Inter, sans-serif; font-size: 0.8rem;
  font-weight: 600; line-height: 1.3; white-space: nowrap;
}
.own-chip svg { flex-shrink: 0; width: 13px; height: 13px; }

/* ── FEE EXPLAINERS (js/fee-tip.js) ──
   Narrow / no-hover: the explanation is folded into the fee line after a "·"
   (.fee-inline) — no icon, no button, nothing to tap. The script sets the row
   top-aligned so the amount sits on the label's first line while it wraps.
   Desktop with hover: the 15px circle is unchanged; the BUTTON around it is
   the tap target (44px via a transparent inset box, so the row's height does
   not change). The text is a popover sibling AFTER the row. `hidden` is the
   closed state (click/Escape/outside); .fee-tip--hover is the transient
   hover/focus reveal, which never fights the pinned state. */
.fee-inline { color: #6b7280; }
.fee-tip-btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; margin: 0; cursor: help; color: inherit; font: inherit;
  width: 15px; height: 15px; border-radius: 50%;
}
.fee-tip-btn::before { content: ''; position: absolute; inset: -14.5px; /* 15 + 29 = 44px hit box */ }
.fee-tip-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
.fee-tip-circle { pointer-events: none; }
.fee-tip-row { position: relative; }
.fee-tip {
  display: block; position: absolute; left: 0; top: 100%; z-index: 5; margin: 4px 0 0; max-width: 320px;
  font-family: Inter, sans-serif; font-size: 12.5px; line-height: 1.5; color: #4b5563;
  background: var(--surface, #fff); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.12);
}
.fee-tip[hidden] { display: none; }
/* Hover/focus reveal, on top of the pinned (click) state. The popover element
   is the sibling AFTER .fee-tip-row, so it is targeted from the row. */
.fee-tip-row:has(.fee-tip-btn:hover) + .fee-tip[hidden],
.fee-tip-row:has(.fee-tip-btn:focus-visible) + .fee-tip[hidden],
.fee-tip.fee-tip--hover[hidden] { display: block; } /* JS mirror of hover/focus, for browsers without :has() */
/* Escape while still hovered/focused: dismissed wins over the reveal until the
   pointer or focus leaves and comes back (js/fee-tip.js clears the class). */
.fee-tip-row:has(.fee-tip-btn) + .fee-tip.fee-tip--dismissed[hidden],
.fee-tip.fee-tip--dismissed[hidden] { display: none; }

/* ── EMOJI PICKER ── */
.ep-wrap { position: relative; }
.ep-wrap > textarea { padding-bottom: 2.25rem; }
.ep-btn {
  position: absolute;
  bottom: 0.5rem;
  right: 0.5rem;
  background: none;
  border: 1.5px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  font-size: 1.2rem;
  line-height: 1;
  padding: 0.2rem 0.3rem;
  transition: background 0.15s, border-color 0.15s;
  z-index: 1;
}
.ep-btn:hover { background: #f5f3ff; border-color: var(--border); }
.ep-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.ep-pop {
  position: absolute;
  bottom: 2.5rem;
  right: 0;
  z-index: 200;
}
emoji-picker {
  --background: #ffffff;
  --border-color: #e5e7eb;
  --indicator-color: #5B4FCF;
  --input-border-color: #e5e7eb;
  --input-font-color: #1a1a2e;
  --input-placeholder-color: #9ca3af;
  --outline-color: #5B4FCF;
  --button-hover-background: #f5f3ff;
  --search-background: #ffffff;
  border: 1.5px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.13);
}
@media (max-width: 768px) {
  .ep-btn, .ep-pop { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .ep-btn { transition: none; }
}

/* ── REEL INTRO OVERLAY WRAPPER ── */
.wishd-reel-wrap { position: relative; display: block; }

/* ── SHARED CONFIRMATION ICON ── */
/* Single source of truth for the green circle + tick used on all success/confirmation screens. */
.wsh-confirm-icon {
  width: 56px;
  height: 56px;
  display: block;
  margin: 0 auto 1rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='56' viewBox='0 0 24 24' fill='none' stroke='%2316A34A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='9 12 11 14 15 10'/%3E%3C/svg%3E");
  background-size: contain;
  background-repeat: no-repeat;
}

/* ── Landing-page CTA button (shared so gifter-thanks can reuse it) ── */
.lp-cta {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: var(--wishd-gradient); color: var(--surface);
  font-size: 0.85rem; font-weight: 600; font-family: 'Poppins', sans-serif;
  padding: 14px 28px;
  border-radius: 50px; text-decoration: none;
  border: 1.5px solid rgba(255,255,255,0.4);
  box-shadow: 0 2px 12px rgba(91,79,207,0.3);
  transition: background 0.18s, transform 0.15s, box-shadow 0.18s;
}
.lp-cta:hover,
.lp-cta:focus-visible {
  background: var(--brand-dark);
  transform: translateY(-2px);
  box-shadow: 0 4px 20px rgba(91,79,207,0.4);
}
.lp-cta:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.lp-cta svg { transition: transform 0.18s; }
.lp-cta:hover svg { transform: translateX(3px); }

/* ── Reel emoji float animation (shared across all surfaces) ── */
@keyframes wishd-emoji-float {
  0%   { transform: translateY(0) rotate(0deg); opacity: 0; }
  8%   { opacity: 1; }
  75%  { opacity: 0.85; }
  100% { transform: translateY(-105vh) rotate(var(--float-rot)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .wishd-float-emoji { display: none !important; }
}

/* ── Wishd doodle wallpaper pattern ──────────────────────────────────────────
   Seamless 1024×1024 tiles, displayed at 512px (half native) so motifs read
   at their designed scale on mobile rather than as a few oversized icons —
   there's no separate desktop mock to size against, so the same ~512px
   target is used at every viewport. background-color is the tile's own
   baked-in warm off-white base (#FBF8F4), shown as a fallback while the
   image loads. Purely decorative (background-image never intercepts
   pointer events) and sits behind all real content — apply to a page body
   or section wrapper, not to something that already has its own opaque
   background further down the stack. The ink motifs are very light lavender
   against a near-white base, so normal body-text colours keep their usual
   AA contrast on top of it.
   .wishd-paper-quiet swaps in the calmer variant (no hearts/balloons/gift
   icons) for any of these surfaces reached in a condolences/in_memory
   context — see wishd_isCondolences() in shared-constants.js.
   The dark variants (wishd-pattern-dark.png / -quiet-dark.png) are gold
   linework on the deep purple brand ground (#5B21B6, matches mailer.ts's
   BRAND_BG) — used only behind reveal.html/demo.html's flat-white phases,
   never as a body-level page background.
   Deliberately placed at the END of this stylesheet: these classes are meant
   to be combined directly with component classes like .card (e.g.
   class="card wishd-paper-quiet"), and .card's shorthand `background:`
   property would reset background-image back to none on any cascade tie —
   living last in source order is what lets these utilities win that tie
   without needing !important. */
.wishd-paper,
.wishd-paper-quiet,
.wishd-paper-dark,
.wishd-paper-quiet-dark {
  background-repeat: repeat;
  background-size: 512px 512px;
}
.wishd-paper,
.wishd-paper-quiet {
  background-color: var(--paper-base);
}
.wishd-paper { background-image: url('/img/pattern/wishd-pattern-light.png'); }
.wishd-paper-quiet { background-image: url('/img/pattern/wishd-pattern-quiet-light.png'); }
.wishd-paper-dark,
.wishd-paper-quiet-dark {
  background-color: var(--paper-base-dark);
}
.wishd-paper-dark { background-image: url('/img/pattern/wishd-pattern-dark.png'); }
.wishd-paper-quiet-dark { background-image: url('/img/pattern/wishd-pattern-quiet-dark.png'); }
