:root {
  /* "Warm Sunset" palette — a richer terracotta/coral primary, warm
     ink/borders, and section colors drawn from the same warm spectrum
     (rose -> coral -> peach -> brown -> amber -> gold -> tan) instead of the
     previous cool-pastel rainbow (mint/lavender/sky). Variable names are kept
     as-is (mint/lav/sky/etc.) so no HTML/JS had to change — only the values. */
  --coral: #e2703a;
  --coral-dark: #c25a2b;
  --cream: #fdf6ec;
  --ink: #3d2b22;
  --muted: #96806f;
  --card: #ffffff;
  --border: #f0ddc9;
  --green: #7a9450;

  /* Section palette — each favorites section / card gets one of these */
  --pink-bg: #fbdfe1;
  --pink-ink: #b1435c;
  --mint-bg: #fbecd3;
  --mint-ink: #a67a2c;
  --lav-bg: #fce0d2;
  --lav-ink: #c85a2e;
  --peach-bg: #fde3cc;
  --peach-ink: #b8701f;
  --sky-bg: #f7e9dc;
  --sky-ink: #8f6a52;
  --butter-bg: #fbe9b8;
  --butter-ink: #96731f;
  --latte-bg: #f0ddc9;
  --latte-ink: #8a5a35;
}
* { box-sizing: border-box; }
/* Native app lock (Face ID/Touch ID) -- see enforceNativeAppLock/showAppLockOverlay
   in app.js. Fixed full-screen cover, high z-index so it sits above
   absolutely everything else (modals, nav, toasts) since the whole point is
   that nothing underneath is usable until it's dismissed. */
#appLockOverlay {
  position: fixed; inset: 0; z-index: 9999;
  background: linear-gradient(160deg, var(--bg-1, #fdece2) 0%, var(--bg-2, #fbdccb) 35%, var(--bg-3, #fbe7c8) 70%, var(--bg-4, #fdf6ec) 100%);
  display: flex; align-items: center; justify-content: center;
}
.app-lock-card { text-align: center; color: var(--ink); }
.app-lock-card svg { display: block; margin: 0 auto 14px; color: var(--coral); }
.app-lock-card p { font-size: 16px; font-weight: 600; margin: 0 0 18px; }
.app-lock-card button { padding: 10px 20px; }
body {
  margin: 0;
  font-family: "EB Garamond", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* --bg-1..4 default to the Warm Sunset gradient; a profile page can override
     them (see profile.html) to tint the background toward the page owner's
     favorite color. */
  background: linear-gradient(160deg, var(--bg-1, #fdece2) 0%, var(--bg-2, #fbdccb) 35%, var(--bg-3, #fbe7c8) 70%, var(--bg-4, #fdf6ec) 100%);
  color: var(--ink);
}
/* Accessibility (Settings -> Accessibility) -- app.js's applyAccessibilityPrefs
   sets these on <body> for every signed-in user on every page. Most rules in
   this file use hardcoded px font sizes rather than rem units, which rules out
   a simple root font-size percentage trick for "larger text" -- zoom scales
   the whole rendered page (layout included) instead, uniformly, without
   touching every individual rule. Supported in Chromium and Safari (desktop
   and iOS), not Firefox -- an acceptable gap for this app's audience, and a
   no-op there rather than a broken one (text just stays at "normal" size). */
body[data-text-size="large"] { zoom: 1.15; }
body[data-text-size="xlarge"] { zoom: 1.3; }
/* Darkens the app's secondary/muted text color (labels, hints, timestamps --
   anything currently styled with var(--muted)) toward the same dark ink used
   for primary text, and thickens body copy slightly, without needing to touch
   every rule that references --muted individually. */
body.bold-text {
  --muted: #5c4632;
  font-weight: 500;
}
body.bold-text h1, body.bold-text h2, body.bold-text h3 {
  font-weight: 800;
}
.app-shell {
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Site-wide brand header — appears at the top of every page */
.brand-header {
  text-align: center;
  padding: 16px 20px 10px;
  background: linear-gradient(90deg, var(--pink-bg), var(--lav-bg), var(--sky-bg), var(--mint-bg));
  border-bottom: 1px solid var(--border);
}
.brand-header .brand-name {
  margin: 0;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.brand-header .brand-name .heart { margin-right: 4px; }
.brand-header .brand-tag {
  margin: 2px 0 0;
  font-size: 11px;
  color: var(--muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Dismissible "you're testing a beta" strip -- folded into renderBrandHeader()
   in app.js so it shows under the brand header on every page automatically. */
.beta-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 8px 16px;
  background: var(--butter-bg);
  color: var(--butter-ink);
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  border-bottom: 1px solid var(--border);
}
.beta-banner span {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: center;
}
.beta-banner svg { flex-shrink: 0; }
.beta-banner a {
  color: inherit;
  text-decoration: underline;
}
.beta-banner button {
  background: none;
  border: none;
  padding: 4px;
  margin: 0;
  color: inherit;
  cursor: pointer;
  display: flex;
  flex-shrink: 0;
  opacity: 0.7;
}
.beta-banner button:hover { opacity: 1; }

header.topbar {
  padding: 14px clamp(20px, 4vw, 56px) 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border);
  background: rgba(255,255,255,0.6);
  position: sticky;
  top: 0;
  z-index: 10;
  backdrop-filter: blur(6px);
}
header.topbar h1 {
  font-size: 18px;
  margin: 0;
  color: var(--ink);
  flex-shrink: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Primary navigation now lives in the fixed bottom tab bar (see .bottom-nav
   below) -- the topbar is just the page title. */

/* Still used for the friend-request/family-request/announcement rows that
   moved from the old notification-bell dropdown onto the Home page. */
.notif-item { padding: 8px; border-radius: 10px; }
.notif-item:hover { background: rgba(0,0,0,0.03); }

main {
  flex: 1;
  padding: 20px clamp(20px, 4vw, 56px) 96px;
}

/* ---------- Bottom tab bar ---------- */
/* Mobile-app-style primary nav: Home / Find Friends / Account (profile
   picture), plus a small Admin tab for admins. Fixed to the bottom of the
   viewport on every page (see the #bottomNav container each page renders
   into). The unseen-notifications indicator that used to be a badge on a
   bell icon is now a single dot on the Home tab -- see renderBottomNav /
   initNotifications in app.js. */
.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: flex;
  justify-content: center;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--border);
  padding: 4px clamp(20px, 4vw, 56px) calc(4px + env(safe-area-inset-bottom, 0px));
}
.bottom-nav-inner {
  display: flex;
  width: 100%;
  max-width: 420px;
  justify-content: space-around;
}
.bottom-nav-item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: transparent;
  border: none;
  color: var(--muted);
  text-decoration: none;
  padding: 6px 16px;
  border-radius: 12px;
  cursor: pointer;
}
.bottom-nav-item.active { color: var(--coral-dark); }
.bottom-nav-item svg { width: 22px; height: 22px; }
.bottom-nav-label { font-size: 10px; font-weight: 700; }
.bottom-nav-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid transparent;
  display: block;
}
.bottom-nav-avatar.initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--lav-bg);
  color: var(--ink);
  font-size: 11px;
  font-weight: 800;
}
.bottom-nav-item.active .bottom-nav-avatar { border-color: var(--coral-dark); }
.bottom-nav-dot {
  position: absolute;
  top: 2px;
  right: 14px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--coral);
  box-shadow: 0 0 0 2px #fff;
}
.footer-link-wrap {
  text-align: center;
  padding: 24px 0 8px;
}
.footer-link-wrap a {
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
}
.footer-link-wrap a:hover { color: var(--coral-dark); }
nav.tabbar {
  position: sticky;
  bottom: 0;
  display: flex;
  border-top: 1px solid var(--border);
  background: #fff;
}
nav.tabbar a {
  flex: 1;
  text-align: center;
  padding: 12px 4px;
  text-decoration: none;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}
nav.tabbar a.active { color: var(--coral-dark); }
nav.tabbar a .icon { display: block; font-size: 18px; margin-bottom: 2px; }

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 16px;
  margin-bottom: 14px;
}
.card h3 { margin: 0 0 4px; font-size: 15px; }
.muted { color: var(--muted); font-size: 13px; }

/* Pastel card variants — used to color-code each section of the profile */
.card.pastel-pink   { background: var(--pink-bg);   border-color: transparent; }
.card.pastel-mint   { background: var(--mint-bg);   border-color: transparent; }
.card.pastel-lav    { background: var(--lav-bg);    border-color: transparent; }
.card.pastel-peach  { background: var(--peach-bg);  border-color: transparent; }
.card.pastel-sky    { background: var(--sky-bg);    border-color: transparent; }
.card.pastel-butter { background: var(--butter-bg); border-color: transparent; }
.card.pastel-latte  { background: var(--latte-bg);  border-color: transparent; }
.card.pastel-pink h3   { color: var(--pink-ink); }
.card.pastel-mint h3   { color: var(--mint-ink); }
.card.pastel-lav h3    { color: var(--lav-ink); }
.card.pastel-peach h3  { color: var(--peach-ink); }
.card.pastel-sky h3    { color: var(--sky-ink); }
.card.pastel-butter h3 { color: var(--butter-ink); }
.card.pastel-latte h3  { color: var(--latte-ink); }
.card[class*="pastel-"] input,
.card[class*="pastel-"] select,
.card[class*="pastel-"] textarea {
  background: rgba(255,255,255,0.75);
  border-color: rgba(0,0,0,0.08);
}

label { display: block; font-size: 13px; font-weight: 600; margin: 12px 0 6px; }
label.checkbox-row { display: flex; align-items: center; font-weight: 500; }
input[type=text], input[type=email], input[type=password], input[type=date], textarea, select {
  width: 100%;
  padding: 11px 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  font-size: 15px;
  font-family: inherit;
  background: #fff;
}
/* Mobile Safari renders <input type="date"> with its own native chrome by
   default -- centered text, a noticeably bigger font, extra internal
   padding -- and mostly ignores the font-size/padding above unless the
   native appearance is explicitly turned off first. Without this, the
   birthday/anniversary/other-date fields render two to three times taller
   than every other input on an iPhone even though the CSS is identical. */
input[type=date] {
  -webkit-appearance: none;
  appearance: none;
  font-size: 14px;
  line-height: 1.3;
  /* Same issue as a.name-link below: an unstyled native date input falls
     back to the browser/OS's own default text color for its value (a
     system blue on iOS/Android) instead of inheriting the site's ink
     color, since color isn't something -webkit-appearance:none resets on
     its own. -webkit-text-fill-color is needed too -- iOS Safari
     specifically paints date/time input text through that property
     rather than plain color once the native appearance is turned off. */
  color: var(--ink);
  -webkit-text-fill-color: var(--ink);
}
/* Long values (a full custom favorite, a long place name, etc.) truncate with
   an ellipsis instead of overflowing their box when not focused -- hover
   shows the full text via the title attribute, kept in sync as you type
   (see app.js). That hover doesn't exist on touch devices, though, so the
   ellipsis is dropped entirely while actually focused/editing -- tapping
   into a field lets you scroll through the real value via the cursor
   instead of staying visually clipped the whole time. */
input[type=text], input[type=tel] {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
input[type=text]:focus, input[type=tel]:focus {
  overflow: visible;
  text-overflow: clip;
}
textarea { resize: vertical; min-height: 60px; }

/* Auto-growing, rows=1-by-default textareas for the "your go-to order" add
   form field (see autoGrowTextarea in app.js) -- unlike a normal textarea
   above, there's no fixed min-height or resize handle, since the JS is what
   grows it to fit whatever's actually typed. */
textarea.order-textarea {
  resize: none;
  min-height: unset;
  overflow: hidden;
}

button, .btn {
  display: inline-block;
  background: var(--coral);
  color: #fff;
  border: none;
  border-radius: 10px;
  padding: 11px 16px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}
button:hover, .btn:hover { background: var(--coral-dark); }
button.secondary, .btn.secondary {
  background: #fff;
  color: var(--coral-dark);
  border: 1px solid var(--coral);
}
button.ghost { background: transparent; color: var(--muted); }
button:disabled { opacity: 0.6; cursor: default; }
.btn-row { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

.form-wrap {
  padding: 24px 8px 8px;
  text-align: center;
  max-width: 420px;
  margin: 0 auto;
}
.form-wrap .logo { font-size: 34px; margin-bottom: 4px; }
.form-wrap h2 { margin: 0 0 4px; color: var(--coral-dark); }
.form-wrap p.sub { color: var(--muted); margin: 0 0 20px; font-size: 14px; }
.form-card { text-align: left; }

.error-box {
  background: #fdeae0;
  color: var(--coral-dark);
  border: 1px solid #f2c6ac;
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: 12px;
}
.switch-link { margin-top: 16px; font-size: 13px; color: var(--muted); }
.switch-link a { color: var(--coral); font-weight: 600; text-decoration: none; }

.avatar {
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--coral);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 16px;
  flex-shrink: 0;
  overflow: hidden;
  object-fit: cover;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* Wraps an .avatar so a small "currently online" dot (admin Users tab only
   -- see isUserOnline/lastActiveAt in db.js) can be positioned relative to
   it without disturbing the avatar's own sizing/centering rules above. */
.avatar-wrap { position: relative; flex-shrink: 0; }
.online-dot {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #2ecc71;
  border: 2px solid #fff;
}
.row { display: flex; align-items: center; gap: 12px; }
.between { justify-content: space-between; }
.user-row { padding: 10px 0; border-bottom: 1px solid var(--border); }
.user-row:last-child { border-bottom: none; }
/* Not scoped to .user-row -- the home feed's activity cards use the same
   class outside that wrapper, and were falling through to the browser's
   default blue link color without this. */
a.name-link { color: var(--ink); text-decoration: none; font-weight: 600; }
a.name-link:hover { color: var(--coral-dark); }

.tag {
  display: inline-block;
  background: #fdeee1;
  color: var(--coral-dark);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  padding: 3px 9px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.tag.green { background: #eef2df; color: var(--green); }
.tag.gray { background: #f1ece5; color: var(--muted); }
.tag.pink { background: var(--pink-bg); color: var(--pink-ink); }

/* Admin > Favorite Categories list -- each row's icon/label/section-tag/
   preset-count used to sit in one no-wrap flex line, so on a phone-width
   screen the section tag (e.g. "OTHER FAVORITES BLOCK") got squeezed below
   its own text width and wrapped internally onto 2-3 lines depending on how
   long that particular label was ("FOOD BLOCK" wrapping to 2 lines,
   "OTHER FAVORITES BLOCK" to 3) -- since each row's height comes straight
   from how many lines its tag wrapped to, rows ended up visibly different
   heights next to each other. Letting the row wrap as whole chips (rather
   than each chip's own text wrapping) plus keeping every tag single-line
   fixes both: same-height pills, and any that don't fit drop cleanly to
   their own line instead of collapsing into ragged multi-line text. */
.cat-row-meta { flex-wrap: wrap; row-gap: 6px; }
.cat-row-meta .tag { white-space: nowrap; flex-shrink: 0; }

.fav-item { margin-bottom: 12px; }
.fav-item .fav-label,
.fav-item .fav-value {
  overflow-wrap: break-word;
  max-width: 100%;
}
.fav-item .fav-label { font-size: 12px; color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.fav-item .fav-value { font-size: 16px; margin-top: 2px; color: var(--ink); }
.empty-state { text-align: center; color: var(--muted); padding: 30px 10px; font-size: 14px; }
.section-title { font-size: 13px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; margin: 18px 0 8px; }
.section-title:first-child { margin-top: 0; }

/* Segmented sub-tabs (My Faves / Friends / Wishlist) */
.seg-tabs {
  display: flex;
  background: rgba(0,0,0,0.04);
  border-radius: 12px;
  padding: 4px;
  margin-bottom: 16px;
}
.seg-tabs button {
  flex: 1;
  /* Flex items default to a min-width equal to their content's width, which
     is what let a bar with enough tabs (e.g. admin's 6, or profile's 5 once
     Settings joined the row) overflow its rounded pill container instead of
     shrinking evenly -- the last tab (often "Feedback") got pushed past the
     edge instead of everyone just getting a little narrower. min-width: 0
     lets flex:1 actually enforce equal widths, with the label itself
     ellipsizing if a tab is ever too narrow to fit its full text. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: transparent;
  color: var(--muted);
  border: none;
  border-radius: 9px;
  padding: 9px 6px;
  font-size: 13px;
  font-weight: 700;
}
.seg-tabs button.active {
  background: #fff;
  color: var(--coral-dark);
  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.seg-tabs button:hover:not(.active) { background: transparent; }

/* Admin page's section switcher -- a dropdown instead of the pill-row style
   above, since admin has more sections (six, with some long labels) than the
   pill bar can comfortably fit without either overflowing or squeezing every
   label into an ellipsis. */
.admin-tab-select {
  width: 100%;
  margin-bottom: 16px;
  font-weight: 700;
  font-size: 14px;
  color: var(--coral-dark);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 36px 12px 14px;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a5a35' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px 16px;
}

/* Field with a visibility toggle */
.field-row { margin-bottom: 14px; }
.field-row .field-top { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; margin-bottom: 6px; }
.field-row label { margin: 0; }
.toggle {
  position: relative;
  display: inline-block;
  width: 38px;
  height: 22px;
  flex-shrink: 0;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle .slider {
  position: absolute; cursor: pointer; inset: 0;
  background: #ddd2c4; border-radius: 999px; transition: 0.15s;
}
.toggle .slider:before {
  position: absolute; content: "";
  height: 16px; width: 16px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%; transition: 0.15s;
}
.toggle input:checked + .slider { background: var(--coral); }
.toggle input:checked + .slider:before { transform: translateX(16px); }
.toggle-label { font-size: 11px; color: var(--muted); font-weight: 600; }

/* Wishlist */
.wishlist-item { padding: 12px 0; border-bottom: 1px solid rgba(0,0,0,0.06); }
.wishlist-item:last-child { border-bottom: none; }
.wishlist-item.has-thumb { display: flex; gap: 10px; align-items: flex-start; }
.wishlist-item .wl-thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 10px;
  flex-shrink: 0;
  background: rgba(0,0,0,0.05);
}
.wishlist-item .wl-body { flex: 1; min-width: 0; }
.wishlist-item .wl-name {
  font-weight: 700;
  font-size: 15px;
  overflow-wrap: break-word;
}
.wishlist-item .wl-meta {
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
  overflow-wrap: break-word;
}
.wishlist-item .wl-link { font-size: 12px; color: var(--coral); text-decoration: none; word-break: break-all; }
.wishlist-item .wl-actions { margin-top: 8px; }

/* Food place entries */
.food-entry {
  background: rgba(255,255,255,0.75);
  border-radius: 12px;
  padding: 10px 12px;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.food-entry .food-place,
.food-entry .food-order {
  overflow-wrap: break-word;
  max-width: 100%;
}
.food-entry .food-place { font-weight: 700; font-size: 14px; }
.food-entry .food-order { font-size: 13px; color: var(--muted); margin-top: 1px; }
.food-entry .food-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

/* Photo upload -- the picture itself is just a clickable image (tap/click to
   see it full-size via the lightbox below); "Change photo" is a separate
   small pill underneath so the edit icon never sits on top of the photo. */
.photo-upload {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  width: 64px;
  flex-shrink: 0;
}
.photo-upload .avatar { width: 64px; height: 64px; font-size: 22px; cursor: pointer; border: none; padding: 0; }
.photo-upload input[type=file] { display: none; }
.photo-upload .edit-badge {
  background: var(--coral); color: #fff;
  display: flex; align-items: center; justify-content: center; gap: 3px;
  padding: 3px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 700; cursor: pointer; white-space: nowrap;
}

/* Any other avatar that isn't part of the upload widget (viewing a friend's
   or child's photo) is still clickable to enlarge -- just needs the pointer
   cursor since there's no edit badge involved. */
.avatar.avatar-clickable { cursor: pointer; border: none; padding: 0; }

/* Full-size photo view */
.photo-lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(20,12,8,0.82);
  display: flex; align-items: center; justify-content: center;
  padding: 30px;
  animation: lightbox-fade-in 0.15s ease-out;
}
.photo-lightbox img {
  max-width: min(90vw, 480px);
  max-height: 80vh;
  border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.4);
  object-fit: contain;
}
.photo-lightbox-close {
  position: fixed; top: 18px; right: 18px;
  background: rgba(255,255,255,0.15);
  color: #fff; border: none; border-radius: 50%;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.photo-lightbox-close:hover { background: rgba(255,255,255,0.28); }
@keyframes lightbox-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ---------- Generic "add" modal ---------- */
/* Used throughout the Faves editor -- every addable list (a coffee order, a
   favorite drink, another important date, etc.) opens the same small popup
   instead of a permanently-visible add form living somewhere else on the
   page. See openModal()/closeModal() in app.js. */
.modal-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(20,12,8,0.5);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  animation: lightbox-fade-in 0.15s ease-out;
}
.modal-panel {
  background: #fff;
  border-radius: 16px;
  width: 100%;
  max-width: 380px;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.modal-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 16px 0;
}
.modal-panel-header h3 { margin: 0; font-size: 16px; }
.modal-close {
  background: rgba(0,0,0,0.06);
  border: none;
  border-radius: 50%;
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}
.modal-close:hover { background: rgba(0,0,0,0.1); }
.modal-panel-body { padding: 12px 16px 16px; }

/* Small round "+" trigger sitting right next to a mini-label, opening the
   add modal for that specific list. */
.mini-label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 8px 0 4px;
}
.mini-label-row .mini-label { margin: 0; }
.add-chip-btn {
  background: rgba(0,0,0,0.08);
  border: none;
  border-radius: 50%;
  width: 22px; height: 22px;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  color: var(--ink);
}
.add-chip-btn:hover { background: rgba(0,0,0,0.16); }
.add-chip-btn svg { width: 13px; height: 13px; }

/* Autocomplete combobox — type-ahead over preset options with free typing allowed */
.combo-wrap { position: relative; }
.combo-list {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-top: 4px;
  max-height: 190px;
  overflow-y: auto;
  z-index: 30;
  box-shadow: 0 6px 16px rgba(0,0,0,0.1);
  display: none;
}
.combo-list.show { display: block; }
.combo-item {
  padding: 9px 12px;
  font-size: 14px;
  cursor: pointer;
}
.combo-item:hover, .combo-item.active { background: rgba(0,0,0,0.05); }
.combo-item .match { font-weight: 700; color: var(--coral-dark); }

/* Favorite-color "add" form -- text input plus a native color-wheel swatch
   (only rendered for the "colors" field, see renderTagAddForm). The native
   <input type="color"> is what actually gives people the drag-around-a-
   wheel-and-pick-a-shade picker on iOS/Android/desktop, not anything drawn
   here -- this is just sizing it to sit tidily next to the text box. */
.tag-input-row { display: flex; gap: 8px; align-items: center; }
.tag-input-row input[type="text"] { flex: 1; margin: 0; }
.color-wheel-swatch {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  background: none;
}
.color-wheel-swatch::-webkit-color-swatch-wrapper { padding: 4px; }
.color-wheel-swatch::-webkit-color-swatch { border: none; border-radius: 6px; }
.color-wheel-swatch::-moz-color-swatch { border: none; border-radius: 6px; }

/* Small color dot on each favorite-color chip -- lets a saved hex value
   (picked from the wheel above) actually look like the color it is instead
   of just showing as a "#a1b2c3"-looking string next to everyone else's
   plain-text chips. */
.chip-swatch {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(0,0,0,0.15);
  flex-shrink: 0;
}

/* Per-section audience picker */
.audience-picker {
  background: rgba(255,255,255,0.6);
  border-radius: 10px;
  padding: 10px 12px;
  margin-bottom: 14px;
}
.audience-picker select { margin-bottom: 0; min-width: 0; max-width: 100%; }
.audience-friends { margin-top: 8px; max-height: 140px; overflow-y: auto; }
.audience-friend-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  padding: 6px 2px;
  margin: 0;
  text-transform: none;
}
.audience-friend-row input { width: auto; margin: 0; }

/* Multi-value favorite chips */
.chip-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(255,255,255,0.8);
  border: 1px solid rgba(0,0,0,0.08);
  border-radius: 999px;
  padding: 5px 6px 5px 12px;
  font-size: 13px;
  font-weight: 600;
}
.chip-remove {
  background: rgba(0,0,0,0.08);
  border: none;
  color: var(--ink);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
}
.chip-remove:hover { background: rgba(0,0,0,0.15); }

.date-entry {
  background: rgba(255,255,255,0.75);
  border-radius: 12px;
  padding: 10px 12px;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.date-entry .date-label { font-weight: 700; font-size: 14px; }
.date-entry .date-value { font-size: 13px; color: var(--muted); margin-top: 1px; }

.save-status {
  font-size: 12px;
  color: var(--muted);
  text-align: center;
  padding: 6px 0 2px;
}

/* Responsive grid for the four favorites sections — 1 column on a phone,
   2 across on a tablet-ish width, up to 4 side-by-side on a wide desktop
   window, since there are exactly 4 cards. */
.faves-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
  align-items: start;
}
.faves-grid > .card { margin-bottom: 0; }
.faves-grid .card h3 { font-size: 13px; }
.faves-grid .card { padding: 12px; }

/* Family tab — child profile cards */
.family-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  gap: 12px;
  margin-top: 10px;
}
.family-card {
  position: relative;
  background: rgba(255,255,255,0.6);
  border-radius: 14px;
  padding: 14px 8px;
  text-align: center;
}
.family-card-link { text-decoration: none; color: inherit; display: block; }
.family-card .avatar { width: 56px; height: 56px; font-size: 18px; margin: 0 auto 8px; }
.family-card-name {
  font-weight: 700;
  font-size: 13px;
  overflow-wrap: break-word;
  max-width: 100%;
}
.family-card .row-remove {
  position: absolute;
  top: 6px;
  right: 6px;
  background: rgba(0,0,0,0.08);
  border: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

/* Color-coded zones inside the consolidated "Your Favorites" card — each block's
   tint matches the section its contents came from (Dates=mint, Coffee=latte,
   Food=lavender, Other Favorites=peach), so we don't need repeated text
   headers to tell them apart. */
/* On a phone, stack top to bottom; on a tablet-ish width and up, let the 4
   blocks reflow 2-across, and on a wide desktop window go 4-across in a
   single row. Spacing comes from the grid gap, not individual margins. */
/* auto-fit + a minmax floor (same pattern as .faves-grid below) instead of
   flat 1fr columns -- plain "1fr 1fr" / "repeat(4, 1fr)" forces every column
   in a row to the exact same width no matter what's actually inside it, and
   has no floor, so a native <input type="date"> (browsers refuse to shrink
   these below their own built-in minimum, roughly 150-170px, regardless of
   any width/min-width CSS on the input itself) could still end up wider
   than the column it landed in and spill out of the block. Giving every
   column a 220px floor means a column only shrinks as far as its content
   (and that floor) allow, and the grid drops to fewer columns on its own
   rather than ever squeezing one down past that. */
.tint-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 620px) {
  .tint-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
}
/* Grid items default to min-width: auto (= their content's max-content
   size), not 0 -- without overriding that, a native <input type="date">
   or the audience-picker's <select> (whose longest option, "Public (even
   people you're not friends with)", is wider than most tint-blocks' other
   content) can force a block wider than its actual grid column, pushing it
   past the edge of the "Your Favorites" card instead of wrapping/shrinking
   like everything else does. */
.tint-block { border-radius: 14px; padding: 10px; min-width: 0; }
.tint-mint  { background: var(--mint-bg); }
.tint-latte { background: var(--latte-bg); }
.tint-lav   { background: var(--lav-bg); }
.tint-peach { background: var(--peach-bg); }

.mini-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin: 8px 0 4px;
  display: block;
}
.mini-label:first-child { margin-top: 0; }

.bare-input {
  border: none;
  background: transparent;
  padding: 2px;
  font-size: 14px;
  margin: 0 0 4px;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bare-input:focus { background: rgba(255,255,255,0.6); border-radius: 6px; }
/* .bare-input's white-space:nowrap + ellipsis (above) is exactly what was
   clipping the profile bio to one line and hiding whatever didn't fit --
   fine for a single-line <input>, wrong for the bio textarea below, which
   needs to actually wrap so autoGrowBio() (see profile.html) can grow the
   box to fit every line instead of scrolling text sideways out of view. */
.bio-textarea {
  white-space: pre-wrap;
  overflow: hidden;
  text-overflow: unset;
  resize: none;
  min-height: 0;
  line-height: 1.35;
  font-family: inherit;
  display: block;
}

.editable-row {
  background: rgba(255,255,255,0.55);
  border-radius: 10px;
  padding: 7px 9px;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.editable-row:last-child { margin-bottom: 0; }
.editable-row input {
  border: none;
  background: transparent;
  padding: 2px;
  font-size: 14px;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.editable-row input:first-child { font-weight: 700; flex: 1; min-width: 0; }
.editable-row input:nth-child(2) { flex: 1.3; color: var(--muted); min-width: 0; }
.editable-row input:focus { background: rgba(255,255,255,0.8); border-radius: 6px; }
/* The order field is a textarea (see autoGrowTextarea in app.js) rather than
   an input, so it can grow to show everything typed instead of clipping
   with an ellipsis like the input rule above -- same look otherwise. */
.editable-row textarea.food-order-input {
  border: none;
  background: transparent;
  padding: 2px;
  font-size: 14px;
  margin: 0;
  resize: none;
  overflow: hidden;
  flex: 1.3;
  color: var(--muted);
  min-width: 0;
  font-family: inherit;
  line-height: 1.3;
}
.editable-row textarea.food-order-input:focus { background: rgba(255,255,255,0.8); border-radius: 6px; }
.editable-row .row-remove {
  background: rgba(0,0,0,0.08);
  border: none;
  width: 22px; height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
}
.editable-row .cat-badge {
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  background: rgba(255,255,255,0.6);
  border-radius: 6px;
  padding: 2px 6px;
  flex-shrink: 0;
}

.chip { background: rgba(255,255,255,0.6); }
.chip .chip-text {
  cursor: pointer;
  display: inline-block;
  max-width: 220px;
  overflow-wrap: break-word;
  vertical-align: bottom;
}
.your-favorites-intro { margin: 0 0 12px; font-size: 12px; }

/* "Your Favorites" is just a full-width card now that the whole app-shell is
   fluid (see .app-shell) — no special breakout sizing needed here anymore. */

/* Subtle dividers inside the peach "Other Favorites" block, so scent/hobbies,
   the tag groups, and custom favorites read as distinct clusters rather than
   one long undifferentiated list. */
.subgroup {
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid rgba(0,0,0,0.07);
}
.subgroup:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }

.size-row {
  display: flex;
  gap: 6px;
  margin-top: 2px;
}
.size-field {
  flex: 1;
  min-width: 0;
}
.size-icon-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  text-align: center;
  margin-bottom: 2px;
  white-space: nowrap;
}

.icon-field { margin-bottom: 4px; }
.icon-field-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 1px;
}
.icon-field .bare-input { margin: 0; }
.size-field .combo-wrap input {
  width: 100%;
  text-align: center;
  background: rgba(255,255,255,0.4);
  border-radius: 8px;
  padding: 6px 2px;
  margin: 0;
  border: none;
  font-size: 14px;
}
.size-field .combo-wrap input:focus { background: rgba(255,255,255,0.7); }

/* Icon+label header on each color-tinted block, so blocks are identifiable
   without relying on color alone. Colored to match that section's "add more"
   card heading. */
.tint-title {
  font-size: 12px;
  font-weight: 800;
  margin-bottom: 6px;
}
.tint-mint .tint-title  { color: var(--mint-ink); }
.tint-latte .tint-title { color: var(--latte-ink); }
.tint-lav .tint-title   { color: var(--lav-ink); }
.tint-peach .tint-title { color: var(--peach-ink); }

/* Love language badge/select, shown in the header's meta row (see
   .profile-header-meta below) -- margin used to be needed here to stack it
   under the other header lines; now that row itself handles spacing, so
   this just needs to look right sitting inline among the other chips. */
.love-language-wrap {
  display: inline-flex;
}
.love-language-badge {
  display: inline-block;
  width: auto;
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 999px;
  border: none;
  background: var(--peach-bg);
  color: var(--peach-ink);
}

/* Header "meta row" -- birthday, upcoming-date nudge, and love language all
   as same-height pill chips in one wrapping row, instead of each being its
   own stacked block-level line (see renderHeader() in profile.html /
   child.html's header for how these get built). */
.profile-header-meta {
  flex-wrap: wrap;
  gap: 6px 8px;
  margin-top: 8px;
}
.profile-meta-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
  background: rgba(0, 0, 0, 0.045);
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}

/* ---------- Tooltips ---------- */
/* Add data-tooltip="..." to any element (usually an icon-only button or
   label) that needs a one-line explanation on hover/focus. Skipped on touch
   devices with no hover -- those elements should still have a plain `title`
   or visible text as a fallback where possible. */
[data-tooltip] {
  position: relative;
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--ink);
  color: #fff;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  padding: 6px 10px;
  border-radius: 8px;
  max-width: 220px;
  width: max-content;
  text-align: center;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 60;
}
@media (hover: hover) {
  [data-tooltip]:hover::after {
    opacity: 1;
    visibility: visible;
  }
}
[data-tooltip]:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

/* ---------- Skeleton loaders ---------- */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--skeleton-base, #ecdfd4) 25%,
    var(--skeleton-shine, #f8efe6) 50%,
    var(--skeleton-base, #ecdfd4) 75%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
  border-radius: 6px;
}
@keyframes skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.skeleton-line {
  height: 12px;
  margin: 6px 0;
}
.skeleton-circle {
  border-radius: 50%;
  flex-shrink: 0;
}
.skeleton-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
}
.skeleton-row-lines {
  flex: 1;
}
@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
  }
}

/* ---------- Lucide icons ---------- */
/* Inline SVGs from icon() in app.js, used in place of the emoji this app used
   to rely on for favorite-category and section labels. When an icon sits next
   to text, nudge it onto the text baseline and give it a little breathing
   room; icon-only buttons (edit-badge, row-remove, chip-remove) have no
   sibling text, so :last-child drops that margin for them automatically. */
.icon {
  display: inline-block;
  vertical-align: -2px;
  margin-right: 4px;
  flex-shrink: 0;
}
.icon:last-child {
  margin-right: 0;
}

/* ---------- Invite-a-friend card (Find Friends, empty search results) ---------- */
.invite-card {
  border: 1px dashed var(--border);
  border-radius: 14px;
  padding: 14px;
  margin-top: 10px;
}
.invite-card-icon {
  color: var(--coral);
  margin-top: 2px;
}

/* ---------- Like button (home feed: wishlist/favorite/daily-prompt items) ---------- */
.like-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  padding: 2px 4px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
}
.like-btn:hover {
  color: var(--coral);
}
.like-btn.liked {
  color: var(--coral);
}
.like-btn .like-count {
  min-width: 8px;
}

/* ---------- Splash screen (index.html, shown briefly on app open) ---------- */
.splash-body {
  margin: 0;
  min-height: 100vh;
}
.splash-screen {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, #fdece2 0%, #fbdccb 35%, #fbe7c8 70%, #fdf6ec 100%);
  opacity: 1;
  transition: opacity 0.22s ease;
  z-index: 100;
}
.splash-screen.splash-out {
  opacity: 0;
}
.splash-mark {
  text-align: center;
  animation: splash-fade-in-out 1.4s ease-in-out both;
}
.splash-mark .splash-name {
  margin: 0;
  font-size: 40px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--coral-dark);
}
.splash-mark .splash-tag {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
@keyframes splash-fade-in-out {
  0% { opacity: 0; transform: scale(0.96); }
  25% { opacity: 1; transform: scale(1); }
  75% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.02); }
}
@media (prefers-reduced-motion: reduce) {
  .splash-mark {
    animation: none;
    opacity: 1;
  }
}

/* ---------- First-signup feature tour ---------- */
/* A short, skippable walkthrough shown once, right after someone creates an
   account -- see startTour() in app.js. A dimmed backdrop sits behind
   everything except the element being introduced (which gets a bright
   cutout via a box-shadow "spotlight" rather than true clip-path cutout, so
   it still works if the target sits inside a scrolling/overflow container),
   plus a small card near it with the explanation and Next/Skip controls. */
.tour-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(61, 43, 34, 0.45);
  z-index: 80;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.tour-backdrop.tour-visible {
  opacity: 1;
}
.tour-spotlight {
  position: fixed;
  z-index: 81;
  border-radius: 14px;
  box-shadow: 0 0 0 4px var(--coral), 0 0 0 9999px rgba(61, 43, 34, 0.45);
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
  pointer-events: none;
}
.tour-card {
  position: fixed;
  z-index: 82;
  max-width: 300px;
  /* Belt-and-suspenders alongside the JS clamping in startTour(): on a very
     short viewport (e.g. landscape phone) even a correctly-clamped top
     position could still let the card's bottom run off-screen, so cap its
     height and let it scroll internally rather than get cut off with no
     way to reach the buttons. */
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px 18px;
  box-shadow: 0 12px 28px rgba(61, 43, 34, 0.22);
  transition: top 0.25s ease, left 0.25s ease;
}
.tour-card h4 {
  margin: 0 0 6px;
  font-size: 16px;
  color: var(--ink);
}
.tour-card p {
  margin: 0 0 14px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
}
.tour-card-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.tour-skip {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px;
}
.tour-next {
  padding: 8px 16px;
  font-size: 13.5px;
}
.tour-step-count {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 8px;
}

/* ---------- Per-friend activity alert toggles (Friends tab) ---------- */
/* Two small icon buttons per friend row -- opting into a push notification
   for that specific friend's new favorites / wishlist adds, on top of (not
   instead of) the passive home-feed entry everyone already gets. Off by
   default (muted/outline); "active" fills in with the brand color so it
   reads at a glance which alerts are on without needing a label. */
.alert-toggle {
  background: rgba(0,0,0,0.05);
  border: none;
  color: var(--muted);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
}
.alert-toggle:hover { background: rgba(0,0,0,0.1); }
.alert-toggle.active {
  background: var(--coral);
  color: #fff;
}
.alert-toggle .icon { margin-right: 0; }

/* Double-tap-to-like heart burst (see handleDoubleTapLike in home.html) */
.likeable-card {
  position: relative;
  overflow: hidden;
}
.heart-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  color: #fff;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,0.25));
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0.3);
  opacity: 0;
  animation: heart-burst-pop 0.7s ease-out forwards;
  z-index: 5;
}
@keyframes heart-burst-pop {
  0% { transform: translate(-50%, -50%) scale(0.3); opacity: 0; }
  20% { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
  35% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }
}

/* Temporary "you're in the app and something just happened" banner -- see
   showNotifBanner/pollForNewNotifications in app.js. Fixed to the very top
   of the viewport (not the app-shell) so it sits above everything,
   including the bottom nav and any open modal, on every page. Starts
   translated fully off-screen and slides down on ".show"; padding-top adds
   the safe-area inset so it clears the iOS status bar / notch on an
   installed PWA instead of sitting underneath it. */
.notif-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 500;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 12px;
  background: var(--coral);
  color: #fff;
  box-shadow: 0 4px 14px rgba(0,0,0,0.18);
  transform: translateY(-120%);
  transition: transform 0.3s ease;
  cursor: pointer;
}
.notif-banner.show { transform: translateY(0); }
.notif-banner-icon { flex-shrink: 0; margin-top: 1px; }
.notif-banner-title { font-weight: 700; font-size: 14px; }
.notif-banner-body { font-size: 13px; opacity: 0.95; }
