:root {
  /* heyyoink dark rebrand (12 August 2026) - full palette swap from the
     earlier light cream/forest-green/gold theme to the approved dark navy
     background with cyan-to-mint accents. Values sampled directly from the
     approved brand reference sheet (background) and the approved gradient
     (accent), not freehand choices. Variable NAMES were deliberately kept
     the same as before (--green/--green-dark/--gold) even though their
     values are now the brand's cyan/mint, to minimize the size of this
     diff - every rule below that referenced these vars automatically
     picks up the new brand colors with no further change needed. */
  --green: #25c6cb;       /* was #2f5d50 - now brand cyan (primary accent) */
  --green-dark: #1a9ca0;  /* was #1f4038 - darker cyan, used for hover states */
  --gold: #8cdfb4;        /* was #c9a24b - now brand mint (secondary accent) */
  --bg: #05070a;          /* was #faf8f4 - brand's dark navy/near-black background */
  --card: #101922;        /* was #ffffff - elevated dark surface for cards */
  --card-alt: #16212b;    /* new - a second, slightly lighter surface tone for hover/inset states */
  --ink: #f4f3ef;         /* was #1c1c1c - off-white text (sampled from the approved wordmark's "hey") */
  --muted: #93a2ac;       /* was #6b6b6b - muted blue-gray, readable on dark */
  --border: #223039;      /* was #e7e2d8 - subtle dark border */
  --danger: #ff7a63;      /* was implicit #b3413a - brightened so it still reads on a dark background */
  --radius: 16px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  min-height: 100vh;
}

.wrap {
  max-width: 720px;
  margin: 0 auto;
  padding: 24px 18px 80px;
}

.wrap.wide { max-width: 1080px; }

header.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 0 8px;
}

/* heyyoink brand header lockup (added 12 August 2026, updated for the full
   dark rebrand later the same day). The icon on the left is the approved
   mark image, used unaltered (same asset as the app icon/favicon) - now on
   the app's own dark background rather than a separate dark tile, since the
   whole page matches the brand's background now. The "hey" portion is real
   text using --ink (now off-white, so it stays visible against the dark
   page without needing a special tile). The gradient on "yoink" is sampled
   directly from the approved wordmark file's own pixels, not a freehand
   color choice. */
.logo {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 800;
  font-size: 26px;
  letter-spacing: 0.5px;
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
}
/* (Fixed 15 Aug 2026, user report: "heyyoink" reads as one fused word on
   the splash but as two separate words in every page header - brand
   inconsistency.) Root cause: .logo's flex gap above was meant to space the
   icon from the wordmark, but "hey" and the .logo-yoink span used to sit as
   direct children of .logo with no wrapper - flex gap applies between ALL
   flex items, including the anonymous box a bare text node becomes, so it
   silently inserted a gap INSIDE the wordmark too. Fixed in the HTML by
   wrapping "hey" + .logo-yoink together in a single .logo-text span, so
   .logo now has exactly two flex children (icon, text) and the gap only
   ever falls between them, matching the splash's fused "heyyoink". */
.logo-text { display: inline-flex; }
.logo-icon {
  height: 34px;
  width: 34px;
  border-radius: 9px;
  display: block;
}
.logo-yoink {
  background: linear-gradient(90deg, #25c6cb, #8cdfb4);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

h1 { font-size: 28px; margin: 8px 0 4px; }
h2 { font-size: 20px; margin: 0 0 12px; }
p.sub { color: var(--muted); margin: 0 0 24px; font-size: 15px; }

/* santa-reveal.html's #recipientName (17 Aug 2026 full-site audit) - was
   inheriting the generic h2 rule above (20px), rendering smaller than the
   boilerplate "🎉 You're buying for:" h1 (28px) sitting right above it.
   The actual name is the whole point of the page - it should read as the
   dominant element, not the generic heading introducing it. Bumped past
   h1 so it visually wins. */
#recipientName { font-size: 32px; font-weight: 700; color: var(--green); }

/* Intro splash (added 12 August 2026, replaces the earlier inline-hero
   version - user asked for a full-screen sequence: "hey yoink" wordmark up
   top, then a big logo underneath it that spins a few times and slowly
   decelerates to a stop (not a quick pop-in), then the tagline fades in
   below the logo, then "Enter". Wordmark + logo are sized to read as
   roughly half the screen on their own. Auto-continues into the real
   homepage a few seconds later even without a tap (JS timer in
   index.html); tapping anywhere skips instantly. Shown once per tab via
   sessionStorage and skipped entirely for prefers-reduced-motion - see the
   JS. A logo that never stops spinning reads as gimmicky/broken on a
   product meant to feel trustworthy for gift-buying, so this decelerates
   to a firm stop rather than spinning forever. */
.splash {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  background: var(--bg);
  cursor: pointer;
  opacity: 1;
  transition: opacity 480ms ease;
}
.splash.splash-hide { opacity: 0; }
body.splash-active { overflow: hidden; }

.splash-wordmark {
  font-weight: 800;
  font-size: clamp(32px, 9vmin, 68px);
  letter-spacing: 0.5px;
  color: var(--ink);
  /* Same value as .splash-tagline's top margin below, so the three stacked
     elements (wordmark / logo / tagline) sit on one even rhythm. This used
     to be clamp(48px, 13vmin, 110px) - nearly double the gap under the logo,
     which made the spacing read as lopsided. */
  margin: 0 0 clamp(40px, 8vmin, 76px);
  line-height: 1;
  opacity: 0;
  animation: splashFadeUp 500ms ease 100ms both;
}
.splash-logo {
  width: clamp(150px, 34vmin, 320px);
  height: clamp(150px, 34vmin, 320px);
  border-radius: 22%;
  display: block;
  /* No glow (14 Aug 2026). There used to be a teal halo here:
     box-shadow: 0 24px 70px -18px rgba(37, 198, 203, 0.55);
     The mark's own tile (#040608) is all but identical to the page
     background (#05070a), so that halo was the only thing making it read as
     a separate tile at all. Once the logo became crisp vector it stopped
     earning its keep - it softened the edges it was meant to flatter and
     was the most dated element on the page. The mark now sits directly on
     the background, which reads cleaner. Restore the line above if the
     tile ever needs to separate from a lighter backdrop. */
  animation: splashSpinDown 3600ms cubic-bezier(0.16, 0.6, 0.2, 1) 300ms both;
}
.splash-tagline {
  font-size: clamp(24px, 6vw, 36px);
  line-height: 1.2;
  font-weight: 800;
  max-width: 560px;
  margin: clamp(40px, 8vmin, 76px) 0 clamp(30px, 5vmin, 44px);
  opacity: 0;
  animation: splashFadeUp 700ms ease 4300ms both;
}
.hero-accent {
  background: linear-gradient(90deg, #25c6cb, #8cdfb4);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.splash-enter {
  font-family: inherit;
  font-weight: 700;
  font-size: 15px;
  color: #06222a;
  background: linear-gradient(90deg, #25c6cb, #8cdfb4);
  border: none;
  border-radius: 999px;
  padding: 12px 28px;
  cursor: pointer;
  opacity: 0;
  animation: splashFadeUp 700ms ease 5300ms both;
}

/* Spins 4 full rotations then decelerates to a firm, clearly visible stop
   over 3.6s - the cubic-bezier keeps most of the rotation speed through the
   middle and only eases off hard in the final stretch, so the slow-down
   itself is something you can actually watch happen rather than a spin
   that just abruptly cuts off. The end rotation MUST be a multiple of 360
   (1440 = exactly 4 full turns) - an earlier version used 1260 (3.5 turns)
   which landed the logo upside down every single time. Everything
   downstream (tagline, Enter, auto-continue) is timed off this finishing,
   with real pauses in between, so the whole thing reads as deliberate
   brand-building rather than a rushed loading animation nobody has time to
   register. */
@keyframes splashSpinDown {
  0% { transform: scale(0.7) rotate(0deg); opacity: 0; }
  6% { opacity: 1; }
  100% { transform: scale(1) rotate(1440deg); opacity: 1; }
}
@keyframes splashFadeUp {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .splash-logo, .splash-wordmark, .splash-tagline, .splash-enter { animation: none; opacity: 1; }
}

/* First-run "how it works" walkthrough overlay (added 13 Aug 2026) - see
   the HTML comment above #introSteps in index.html for the full reasoning.
   Reuses the splash's fixed full-screen overlay pattern (same z-index,
   same body.splash-active scroll lock) rather than inventing a new one. */
.intro-steps {
  position: fixed;
  inset: 0;
  z-index: 998; /* just under .splash's 999, so a fast-tapper never sees both stacked oddly */
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Top-aligned rather than vertically centred (14 Aug 2026). Centring put
     the heading halfway down the screen with a large dead gap underneath,
     and it moved the heading around between slides depending on how much
     content each one had. Anchoring to the top keeps the heading in the same
     place on all three and gives the content room to breathe below it. */
  justify-content: flex-start;
  text-align: center;
  padding: clamp(72px, 13vh, 150px) 24px 130px;
  background: var(--bg);
  overflow-y: auto;
}
.intro-skip {
  position: fixed;
  top: 18px;
  right: 20px;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  padding: 8px;
}
.intro-skip:hover { color: var(--ink); }
.intro-step {
  display: none;
  max-width: 700px;
  width: 100%;
  animation: splashFadeUp 400ms ease both;
}
.intro-step-active { display: block; }
/* Spacing bumped (13 Aug 2026, matching native's welcome.tsx fix): eyebrow
   now sits below the title (see the HTML), so it needs real breathing room
   on both sides to read as a distinct accent line rather than crowding the
   title above it or the sub-copy below it. */
.intro-eyebrow {
  color: var(--green);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  font-size: 13px;
  margin-top: 4px;
  margin-bottom: 36px;
}
/* Type and spacing bumped 14 Aug 2026 (user feedback: "better spacing,
   slightly bigger script, and the header separated more from the script").
   The heading now clears the content below it by a full 36-40px so it reads
   as a header rather than as the first line of the paragraph, and the body
   copy moved 16px -> 17.5px with looser leading, since these slides are the
   first thing a new visitor reads and were sitting a notch too tight. */
.intro-title { font-size: clamp(28px, 5.4vw, 42px); font-weight: 800; line-height: 1.18; color: var(--ink); margin: 0 0 clamp(30px, 5vw, 48px); }
.intro-sub { font-size: 18.5px; line-height: 1.75; color: var(--muted); max-width: 560px; margin: 0 auto; }
.intro-compare {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 8px;
  text-align: left;
}
.intro-compare-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 22px 20px;
}
/* The "Without heyyoink" card is colour-coded against the calm one (14 Aug
   2026). Previously only the good side was tinted, so the pair read as one
   neutral box and one highlighted box rather than as a before/after. Red on
   the problem side and turquoise on the solution side makes the contrast do
   the explaining before anyone reads a word. --danger is the same red used
   for destructive actions elsewhere, so the palette doesn't grow. */
.intro-compare-chaos { border-color: var(--danger); }
.intro-compare-chaos .intro-compare-label { color: var(--danger); }
.intro-compare-calm { border-color: var(--green); }
.intro-compare-label { font-weight: 700; font-size: 13px; color: var(--muted); text-transform: uppercase; margin-bottom: 16px; }
.intro-compare-calm .intro-compare-label { color: var(--green); }
.intro-compare-card ul { list-style: none; margin: 0; padding: 0; }
.intro-compare-card li { font-size: 15.5px; line-height: 1.7; color: var(--ink); margin-bottom: 15px; }
.intro-features { display: flex; flex-direction: column; gap: 16px; margin-top: 8px; text-align: left; }
.intro-feature {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px 18px;
}
/* Swapped from emoji to a hand-drawn line-icon set (15 Aug 2026, design
   pass) - emoji as functional UI icons was the single biggest "built with
   AI" tell on the page: inconsistent stroke weight, renders differently
   per OS, doesn't take brand color. These are plain stroke SVGs (see the
   HTML) that inherit currentColor, sized and colored here instead. */
.intro-feature-icon { display: inline-flex; width: 22px; height: 22px; color: var(--green); }
.intro-feature-icon svg { width: 100%; height: 100%; }
.intro-feature strong { display: block; font-size: 16.5px; color: var(--ink); margin-bottom: 6px; }
.intro-feature p { font-size: 15px; color: var(--muted); margin: 0; line-height: 1.6; }
.intro-dots { position: fixed; bottom: 78px; left: 50%; transform: translateX(-50%); display: flex; gap: 7px; }
.intro-dot { width: 7px; height: 7px; border-radius: 999px; background: var(--border); transition: background 0.2s ease; }
.intro-dot-active { background: var(--green); width: 20px; }
/* Higher specificity than button.primary (declared later in this file) so
   this doesn't get clobbered by its width:100%/margin-top:18px regardless
   of source order. */
.intro-steps button.intro-next {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  width: auto;
  min-width: 220px;
  margin: 0;
}
@media (max-width: 560px) {
  .intro-compare { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .intro-step { animation: none; }
}

/* Owner dashboard's compact "Shopping from [country]" line (added 12 August
   2026, dark-rebrand contrast fix). This sits inside a p.sub, which is
   var(--muted) by default - fine for the trailing explanatory note, but the
   "Shopping from" label and the select's own text were hard to read at that
   dimness against the near-black background, so both get bumped to
   full-contrast --ink here while the explanatory note after the select
   stays at the softer --muted tone. */
.country-row-label { color: var(--ink); font-weight: 600; }
.country-select {
  display: inline-block;
  width: auto;
  padding: 4px 10px;
  font-size: 13px;
  margin-left: 4px;
  background: var(--card-alt);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
  transition: box-shadow 0.5s ease, border-color 0.5s ease;
}

/* "One list, but for more than one person" card (owner.html) - collapsed to a slim single-line
   link by default (most lists are single-person and never touch this), so
   it shouldn't cost a full card's worth of vertical space until it's
   actually used. Full padding/shadow only kick in once expanded. */
.members-card {
  margin-top: 18px;
  padding: 0;
  border: none;
  box-shadow: none;
  background: transparent;
}
.members-card.members-card-open {
  padding: 22px;
  border: 1px solid var(--border);
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
  background: var(--card);
}
.members-collapsed-link {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  color: var(--green);
  font-weight: 600;
  font-size: 14px;
  padding: 12px 16px;
}
.members-collapsed-link:hover { border-color: var(--green); }

/* Brief arrival glow for the "Start my list" hero CTA (13 Aug 2026) - cues
   that this is exactly where the button just took you, then fades back to
   the plain card look so it doesn't linger and look like a permanent
   highlighted/errored state. */
.card.card-arrival {
  border-color: var(--green);
  box-shadow: 0 0 0 1px var(--green), 0 8px 32px rgba(37, 198, 203, 0.25);
}

/* Persistent light-blue highlight, not a temporary flash (17 Aug 2026, user
   feedback: "There should be a light blue highlight around the box" -
   referring to #startCard, the main create-list card. Distinct from
   .card-arrival above, which fades out after a few seconds - this stays on
   permanently, marking #startCard as the primary action on the page. Uses
   the same --green (brand cyan, i.e. "light blue") as .card-arrival for
   visual consistency, just a lighter/steadier glow rather than an arrival
   flash. */
.card.card-highlight {
  border-color: var(--green);
  box-shadow: 0 0 0 1px var(--green), 0 4px 20px rgba(37, 198, 203, 0.18);
}
/* Same idea for the new standalone Secret Santa / Secret Gifting Group card
   below #startCard (17 Aug 2026, "The santa and secret surprise boxes
   should be in the own separate highlighted box underneath the main box")
   - deliberately softer than #startCard's highlight (thinner glow, no inset
   border-width bump) so it still reads as secondary to the main card, just
   visually set apart from the plain page background rather than blending
   into it. */
.card.card-highlight-soft {
  border-color: rgba(37, 198, 203, 0.4);
  box-shadow: 0 2px 12px rgba(37, 198, 203, 0.1);
}

/* Standalone auth page layout (login.html - 13 Aug 2026 visual polish
   pass). Vertically centers the small login form in the remaining space
   below the header instead of leaving it stranded at the top of an
   otherwise-empty dark page, and adds a bit of visual presence (the brand
   icon, tighter measure on the card) so it reads as a proper "app" moment
   rather than a bare fallback form. header.top's own height (~76px with its
   padding) is subtracted so the centering accounts for the real available
   space rather than the full viewport. */
.auth-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 140px);
  text-align: center;
}
.auth-icon {
  width: 52px;
  height: 52px;
  border-radius: 14px;
  margin: 0 auto 20px;
  display: block;
  box-shadow: 0 6px 24px rgba(37, 198, 203, 0.25);
}
.auth-page h1 { margin-top: 0; }
.auth-card {
  text-align: left;
  width: 100%;
  max-width: 380px;
  margin: 8px auto 0;
  border-top: 2px solid var(--green);
}
.auth-back { margin-top: 22px; font-size: 12.5px; }

/* Owner dashboard "hero" panel (added 12 August 2026, owner-dashboard wow-
   factor pass) - groups the list title, occasion, shopping-country row,
   trust banner, and the share link into one elevated, gently-glowing panel
   at the very top of the page instead of loose stacked text + a separate
   plain card. This is the single most important area on the whole page
   (it's why an owner opens this link in the first place - to grab and
   share it), so it gets the strongest visual treatment on the page. */
.owner-hero {
  background: linear-gradient(180deg, rgba(37, 198, 203, 0.08), rgba(140, 223, 180, 0.02) 55%), var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px 24px 22px;
  box-shadow: 0 30px 70px -38px rgba(37, 198, 203, 0.45);
  margin-top: 6px;
}
.owner-hero h1 { margin-top: 0; }
/* List title rename (owner.html click-to-edit ✏️) - row keeps the h1/input
   and the edit button aligned on one line regardless of title length. */
.list-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.list-title-row h1 { margin-bottom: 0; }
.list-title-edit-btn { font-size: 15px; line-height: 1; padding: 6px 10px; }
.list-title-input {
  font-size: clamp(24px, 4vw, 34px);
  font-weight: 800;
  color: var(--ink);
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--green);
  padding: 0 0 2px;
  flex: 1 1 260px;
  min-width: 180px;
}
.list-title-input:focus { outline: none; }
.share-box-wrap {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}
.share-box-wrap label { margin-top: 0; }

label { display: block; font-size: 13px; font-weight: 600; color: var(--muted); margin: 14px 0 6px; }
label:first-child { margin-top: 0; }

/* Scoped bigger/bolder treatment for just the "What's this list for?" label
   (17 Aug 2026) - see index.html's matching note on why this is scoped to
   .list-title-label rather than changing the shared `label` rule above,
   which every form on the site uses. 18px/var(--ink) so it actually reads
   as a header over its own 16px textarea, rather than the previous 13px/
   muted styling reading smaller than the content it's labelling. */
.list-title-label { font-size: 18px; font-weight: 700; color: var(--ink); margin-bottom: 8px; }

/* Same label-vs-content mismatch as .list-title-label, applied sitewide
   (17 Aug 2026 full-site audit) - every form label flagged during the
   audit sat at 13px/muted above 16px inputs/textareas, reading smaller
   than the field it describes. Scoped class (not the shared `label` rule)
   so pages/labels not flagged - e.g. modal "Price"/"Section" secondary
   fields, "Share this link" labels which already have their own
   .share-box override - are untouched. Applied across owner.html,
   guest.html, login.html, santa-join.html and santa-organizer.html. */
.field-label { font-size: 15px; font-weight: 700; color: var(--ink); }

input[type=text], input[type=url], input[type=email], select, textarea {
  width: 100%;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  font-size: 16px;
  background: var(--card-alt);
  color: var(--ink);
  font-family: inherit;
}
textarea { resize: vertical; }
/* Product search box (owner.html) - user feedback (16 Aug 2026): with the
   AI-ideas box also visible right below it, people mid-search couldn't
   tell which box to type their search into, and the search field itself
   "got lost" among the surrounding cards/copy. Two fixes: this box now
   visually stands out (thicker accent border, bigger text, more padding)
   as the clearly primary action on the page, and the AI-ideas box below
   it is now collapsed behind a small toggle by default (see .ai-toggle-link
   and #aiCard's default display:none in owner.html) so it's not competing
   for attention while someone's actively searching. */
#searchQuery {
  padding: 15px 16px;
  border: 2px solid var(--green);
  font-size: 17px;
  font-weight: 600;
}
#searchQuery::placeholder { font-weight: 400; opacity: 0.65; }
/* Collapsed "Ask Hey Yoink for ideas" trigger - see #searchQuery comment
   above for why this is collapsed by default. Went through three rounds of
   user feedback (16 Aug 2026): pass 1 was muted/dashed text with no button
   affordance; pass 2 added color + a dashed tint but still read as a note,
   not a control, since one button was doing the job of both an explanation
   AND a call to action, all crammed on one wrapped line. Pass 3 (this one)
   splits it into a proper two-part card - a small quiet question on its
   own line, then a clearly separate, real button underneath - mirroring
   how #aiCard itself is laid out (title, then its own button), just more
   compact. */
.ai-toggle-card {
  margin-top: 18px;
  padding: 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  text-align: center;
}
.ai-toggle-label { margin: 0 0 12px; color: var(--muted); font-size: 14px; }
.ai-toggle-btn {
  display: block;
  width: 100%;
  padding: 13px 20px;
  background: transparent;
  border: 1px solid var(--green);
  border-radius: 10px;
  color: var(--green);
  font-size: 15px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}
.ai-toggle-btn::after { content: " \2192"; }
.ai-toggle-btn:hover { background: rgba(37, 198, 203, 0.1); }
/* The homepage's list-title field is a textarea only so it can grow to show
   a full example (see index.html's autoSizeTitle) - it is a single-value
   field, so the drag handle and scrollbar would both be noise, and its
   height is set in JS. */
#title { resize: none; overflow: hidden; line-height: 1.45; display: block; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--green); outline-offset: 1px; }
/* Extra right-hand breathing room so long typed text doesn't run straight
   under Chrome's built-in "saved password/passkey" key icon on an email
   field (reported live on the login page) - the icon itself is native
   browser chrome and can't be removed via CSS, but this keeps text clear
   of it instead of visually colliding. */
input[type=email] { padding-right: 40px; }

/* Chrome/Safari's autofill styling (yellow/blue background, dark text)
   otherwise stomps straight over the dark theme the instant a browser
   password manager or contact-autofill suggestion fills one of these
   fields in (reported live on the login page's email field, but this
   covers every text/email input site-wide). The huge transition-delay is
   the standard trick for overriding `-webkit-autofill`'s background -
   browsers won't let you set `background` directly on it, but they DO
   respect a box-shadow "faking" one, and a very long transition delay
   stops it flashing back to yellow before the override takes effect. */
input[type=text]:-webkit-autofill,
input[type=url]:-webkit-autofill,
input[type=email]:-webkit-autofill,
input[type=text]:-webkit-autofill:hover,
input[type=url]:-webkit-autofill:hover,
input[type=email]:-webkit-autofill:hover,
input[type=text]:-webkit-autofill:focus,
input[type=url]:-webkit-autofill:focus,
input[type=email]:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0px 1000px var(--card-alt) inset;
  box-shadow: 0 0 0px 1000px var(--card-alt) inset;
  caret-color: var(--ink);
  transition: background-color 600000s 0s, color 600000s 0s;
}

button, a.ghost {
  cursor: pointer;
  font-weight: 700;
  border: none;
  border-radius: 10px;
  padding: 13px 20px;
  font-size: 15px;
  transition: transform 0.05s ease, opacity 0.15s ease;
}
/* a.ghost (13 Aug 2026, user feedback: "Create your own list" on the guest
   page was near-illegible) - .ghost/.small were only ever defined for
   `button`, so every `<a class="ghost small">` on the site (this guest-page
   link, the homepage's "Log in", owner.html's AI search link) was silently
   falling back to the browser's unstyled default link color/underline
   instead of the app's actual button look. Fixing it here at the root
   rather than one-off patching just this instance, since it was a real
   site-wide gap, not a one-page bug. */
a.ghost { display: inline-block; text-decoration: none; }
button:active, a.ghost:active { transform: scale(0.98); }
button.primary { background: var(--green); color: #05070a; width: 100%; margin-top: 18px; }
button.primary:hover { background: var(--green-dark); }
button.primary:disabled { opacity: 0.6; cursor: default; }
/* dashboard.html's list row uses <a class="primary"> for the "Open" link
   (17 Aug 2026 full-site audit) - only button.primary existed above, so
   that link was rendering completely unstyled (no background/color at
   all). Base rule shared with the button so both stay visually
   identical; button-only rules (full-width, top margin, disabled state)
   don't apply to an inline link and are left off. */
a.primary { background: var(--green); color: #05070a; display: inline-block; }
a.primary:hover { background: var(--green-dark); }
button.ghost, a.ghost { background: transparent; color: var(--green); border: 1px solid var(--border); }
button.ghost:hover, a.ghost:hover { border-color: var(--green); }
button.small, a.small { padding: 8px 12px; font-size: 13px; }
button.danger { background: var(--card-alt); color: var(--danger); border: 1px solid #4a2a24; }
button.claim { background: var(--gold); color: #05070a; width: 100%; }
button.claim.claimed { background: var(--card-alt); color: var(--muted); }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 16px;
  margin-top: 20px;
}

.item {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.item:hover {
  transform: translateY(-3px);
  box-shadow: 0 16px 34px -18px rgba(37, 198, 203, 0.45);
  border-color: rgba(37, 198, 203, 0.4);
}
.item .thumb {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--card-alt);
  color: var(--muted);
  font-size: 12px;
  overflow: hidden;
  /* The live app renders product photos as real <img> tags (object-fit:
     cover, below), but index.html's static marketing demo tiles (hero demo
     card + preview-grid) still use inline background-image on this same
     .thumb div. Without background-size/position, a background-image
     defaults to its native pixel size anchored top-left - in a ~160-200px
     box that showed only a tiny, tightly-cropped corner of the 400x400
     photo, which read as a flat abstract color block instead of a
     recognizable product (14 Aug 2026, user catch). cover+center makes the
     background-image path behave the same as the img/object-fit path. */
  background-size: cover;
  background-position: center;
}
/* Product images are now real <img> tags (not CSS background-image) so a
   broken/blocked image (hotlink protection, 404, mixed content) triggers
   onerror and reveals .thumb-fallback text - previously a background-image
   that failed to load just showed a blank square with no explanation.
   See owner.html's itemCardHtml() / renderSearchResults(). */
.item .thumb img, .search-result .thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.thumb-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 6px;
}

/* "Most wanted" star toggle (owner.html only) - sits on the item card's
   thumb, top-right, so it's reachable straight from the grid at any time
   (right after adding an item, or on any existing item later) rather than
   being buried in the edit modal. Not claim-adjacent - purely something the
   owner sets on their own items - so unlike the guest-only claimed badge
   below it's fine for this to live in the owner's own view. */
.mostwanted-toggle {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 1;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: rgba(5, 7, 10, 0.65);
  color: var(--ink);
  font-size: 14px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.mostwanted-toggle:hover { border-color: var(--gold); }
.mostwanted-toggle.mostwanted-on { color: var(--gold); border-color: var(--gold); }
.item .body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.item .title {
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.item .price { font-size: 13px; color: var(--green); font-weight: 700; }
.item a.visit { font-size: 12px; color: var(--muted); text-decoration: none; }
.item a.visit:hover { text-decoration: underline; }
.item .clicks { font-size: 11px; color: var(--muted); }
.item .actions { margin-top: auto; display: flex; gap: 6px; flex-wrap: wrap; }

/* Group chip-in (§62) - "Split this with others" lets several guests pool
   onto one item instead of one person soloing "I've got this." Sits inside
   the same .actions row as the plain claim button (see guest.html's
   itemCardHtml/groupAreaHtml), so it inherits the flex-wrap above rather
   than needing its own layout container for the collapsed state. */
.group-chip-in { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.group-participants { font-size: 12px; color: var(--muted); line-height: 1.4; }
/* Live per-person split amount - recalculated from the current participant
   count on every page render (see groupAreaHtml's currentSplitHtml), so it
   never goes stale the way a one-off shared text message would. Slightly
   smaller/dimmer than .group-participants since it's a supporting detail,
   not the primary line. */
/* Opacity(0.8) stacked on top of an already-dim var(--muted) color pushed
   this below comfortable contrast (17 Aug 2026 full-site audit). Dropped
   the opacity and let the color alone carry the "supporting detail" tone
   - still visibly lighter than .group-participants, just legible. */
.group-current-split { font-size: 11.5px; color: var(--muted); line-height: 1.3; margin-top: -2px; }
/* Stacked vertically, not side-by-side - a narrow item card (min 160px)
   has no room for a text input next to a button that's separately forced
   to width:100% (see button.claim above), which was crushing the input
   down to an unusably tiny, near-invisible box. Stacking avoids the width
   conflict entirely and matches how every other action in this card
   (claim/split buttons) is already a full-width row. */
.group-name-form { display: flex; flex-direction: column; gap: 6px; width: 100%; margin-top: 4px; }
.group-name-input {
  width: 100%;
  min-width: 0;
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  color: var(--ink);
  font-size: 13px;
  box-sizing: border-box;
}
.group-name-input:focus { outline: none; border-color: var(--green); }

/* "Cash fund" item type (§63 roadmap item 2). --- */

/* Add-item tab toggle (owner.html's search card) - "Product link" (the
   existing search flow) vs. "Cash fund" (title + goal-amount form). Small,
   pill-style tabs, same visual weight as a ghost button but grouped so it
   reads as one control rather than two separate buttons. */
.item-type-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 14px;
}
.item-type-tab {
  flex: 1;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 9px 10px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.item-type-tab:hover { border-color: var(--green); color: var(--ink); }
.item-type-tab.active {
  background: rgba(37, 198, 203, 0.12);
  border-color: var(--green);
  color: var(--green);
}

/* Fund placeholder tile - stands in for a product thumbnail on a fund item
   (owner and guest cards alike), since there's no product image to show.
   Reuses .thumb's own sizing/position (aspect-ratio, position:relative for
   the star/badge overlays) and just swaps in a centered emoji instead of an
   <img>. */
.fund-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
}
.fund-thumb-icon { font-size: 40px; }

/* "Goal: $X" - same visual weight/placement as .item .price (see above),
   just a distinct class so it's clear at a glance in devtools which items
   are funds vs. priced products. */
.item .fund-goal { font-size: 13px; color: var(--green); font-weight: 700; }

/* "Be the first to chip in" - sits where the "X people chipping in" summary
   line normally goes (see .group-participants above), shown only until a
   fund actually has its first participant. */
.fund-cta { color: var(--gold); font-weight: 600; }

/* Payment instructions (§65 follow-up) - shown to guests only once a fund
   has a participant, appended after the group summary/leave/invite block.
   Subtle boxed treatment so it reads as "practical info" rather than
   competing visually with the chip-in action buttons above it. */
.fund-payment-info {
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px dashed var(--border);
  border-radius: 10px;
  font-size: 12.5px;
  color: var(--muted);
}

.ai-card { border-color: #1e3a3c; background: linear-gradient(180deg, #0d1a1c, var(--card)); }
.ai-note {
  margin-top: 14px;
  font-size: 13px;
  color: var(--muted);
  background: var(--lightShade, var(--card-alt));
  border-radius: 10px;
  padding: 12px 14px;
}
.ai-note code { background: #1b2830; padding: 1px 5px; border-radius: 4px; font-size: 12px; }
.ai-ideas { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }
.ai-idea {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
}
.ai-idea-name { font-weight: 700; font-size: 14px; }
.ai-idea-why { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.ai-search-btn { white-space: nowrap; text-decoration: none; }

.budget-card {
  /* Padding aligned to the generic .card's 22px all round (17 Aug 2026
     full-site audit) - was 18px 22px with no apparent reason for the
     tighter top/bottom, just an inconsistency vs every other card on
     the site. */
  padding: 22px;
  background: linear-gradient(180deg, rgba(140, 223, 180, 0.07), var(--card) 60%);
  border-color: #223d2c;
}
.budget-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.budget-total { font-size: 26px; font-weight: 800; color: var(--green); }
.budget-note { font-size: 12px; color: var(--muted); margin: 10px 0 0; }

.price-drop { font-size: 12.5px; font-weight: 700; }
.price-drop-was { color: var(--muted); font-weight: 500; text-decoration: line-through; }
.price-drop .price { display: inline; font-size: 12.5px; }
.dup-note { font-size: 11px; color: #e0c46a; background: #2b2412; border-radius: 6px; padding: 3px 7px; display: inline-block; width: fit-content; }

.search-results-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 14px;
  margin-top: 16px;
}
.search-result {
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.search-result .thumb {
  /* Was a plain <div>; now an <a> (13 Aug 2026 - see HANDOVER.md, "view
     before adding" fix) so the thumbnail itself opens the real product page.
     display:block keeps aspect-ratio working the same as before, since
     anchors are inline by default and aspect-ratio doesn't apply to those. */
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--card);
  color: var(--muted);
  font-size: 11px;
  overflow: hidden;
}
.search-result .body { padding: 10px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.search-result .title {
  /* Also now an <a> (see .thumb comment above) - reset link styling so it
     still reads as plain card text, not a blue underlined link. */
  display: -webkit-box;
  color: inherit;
  text-decoration: none;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.search-result .price { font-size: 12.5px; color: var(--green); font-weight: 700; }
.search-result a.visit.small {
  font-size: 11.5px;
  color: var(--green);
  font-weight: 600;
  text-decoration: none;
}
.search-result a.visit.small:hover { text-decoration: underline; }
.search-result select.search-result-member {
  width: 100%;
  font-size: 12px;
  padding: 6px 8px;
}
.search-result button { margin-top: auto; width: 100%; }

/* Edit-item modal (native/web parity item 2 - no prior web pattern for a
   modal existed, so this is a small, purpose-built one rather than reusing
   something else). Kept independent of the items grid's own re-render (see
   owner.html's editItemId handling) so it isn't clobbered by the
   pending-item poll while the owner is mid-edit. */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.55);
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 50;
}
.modal-overlay.show { display: flex; }
.modal-box {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px;
  width: 100%;
  max-width: 420px;
  max-height: 90vh;
  overflow-y: auto;
}
.modal-box h2 { margin-top: 0; }
.modal-actions {
  display: flex;
  gap: 10px;
  margin-top: 18px;
}
.modal-actions button.primary { margin-top: 0; flex: 1; }
.modal-actions button.ghost { flex: 1; }

/* "Most wanted" badge (guest.html) - independent of, and additive to, the
   claimed/splitting badge below: an item can be most-wanted AND unclaimed,
   claimed, or splitting, all at once, so this never replaces that badge.
   Lives inside .thumb (already position:relative - see .item .thumb above)
   rather than on .item itself, and sits at the opposite corner from the
   claimed-overlay's top-left ::after label so the two never overlap when
   both are showing on the same card. */
.badge-mostwanted {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(0,0,0,0.7);
  color: var(--gold);
  font-size: 11px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 999px;
  white-space: nowrap;
}

.claimed-overlay {
  position: relative;
}
.claimed-overlay::after {
  /* Was a hardcoded "Claimed" string - CSS content can't call Yoink18n.t(),
     so the translated label is written onto a data attribute in JS instead
     (see renderItems() in guest.html) and pulled in here via attr(). */
  content: attr(data-claimed-label);
  position: absolute;
  top: 8px; left: 8px;
  background: rgba(0,0,0,0.7);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 999px;
}

/* 17 Aug 2026, user feedback (via a friend testing owner.html's "Share this
   link with family & friends" box): "thicker box or much bolder... too much
   inline size wise with the above" - i.e. this box read as visually weak
   compared to #searchQuery just above it on the same page, which already
   got the identical "thicker accent border, bigger bold text, more padding"
   treatment on 16 Aug 2026 for the same reason. Applying that same proven
   fix here, at the shared .share-box level rather than one-off patching
   owner.html, since santa-join.html's "your private link" box and
   santa-organizer.html's "share this link so people can join" box use this
   exact same input+copy-button structure and read exactly as quiet - all
   three are equally important "don't lose this" moments, not just owner.html's. */
.share-box {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}
.share-box input {
  flex: 1;
  font-size: 15px;
  font-weight: 700;
  border: 2px solid var(--green);
  padding: 13px 14px;
}
.share-box button.ghost,
.share-box a.ghost {
  border: 2px solid var(--green);
  font-weight: 800;
  padding: 13px 18px;
}

.empty {
  text-align: center;
  padding: 50px 20px;
  color: var(--muted);
}
.empty-icon { font-size: 38px; margin-bottom: 12px; }

.about-card { margin-bottom: 18px; }
.about-event-date { font-size: 13px; font-weight: 700; color: var(--green); margin-bottom: 6px; }
.about-bio { font-size: 14.5px; line-height: 1.5; margin: 0 0 10px; color: var(--ink); }
.about-interests { display: flex; flex-wrap: wrap; gap: 6px; }
.interest-tag {
  font-size: 12px;
  font-weight: 600;
  background: var(--card-alt);
  color: var(--gold);
  padding: 4px 10px;
  border-radius: 999px;
}

.banner {
  background: #0f2420;
  border: 1px solid #1e3d37;
  color: var(--gold);
  border-radius: 12px;
  padding: 12px 16px;
  font-size: 13.5px;
  margin-bottom: 18px;
}
/* Second placement of the same banner style, directly above the items grid
   - see owner.html's grid-note comment. */
.banner.grid-note { margin-top: 18px; }
/* "Change" flow's replacing-banner (item-edit redesign, HANDOVER.md §39) -
   toggled between display:none/flex in owner.html's JS, so this just
   handles the row layout once it's shown. */
#replacingBanner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.toast {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--card-alt);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 13.5px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

/* Floating "View my list" button (owner.html) - only shown via JS while
   the search card is scrolled into view; see initViewListFab(). */
.view-list-fab {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--green);
  color: #05070a;
  border: none;
  border-radius: 999px;
  padding: 12px 18px;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 10px 28px -10px rgba(37, 198, 203, 0.55);
}
.view-list-fab:hover { background: var(--green-dark); }
.view-list-fab-count { font-weight: 800; }
@media (max-width: 520px) {
  .view-list-fab { right: 14px; bottom: 14px; padding: 11px 16px; font-size: 13px; }
}
.toast.show { opacity: 1; }

@media (max-width: 420px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

/* --- Custom date picker (replaces the native <input type="date">) --- */
.datepicker { position: relative; }
.datepicker-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--card-alt);
  color: var(--ink);
  font-size: 16px;
  font-weight: 400;
  font-family: inherit;
  text-align: left;
}
.datepicker-trigger:hover { border-color: var(--green); }
.datepicker-trigger .datepicker-placeholder { color: var(--muted); }
.datepicker-icon { font-size: 15px; opacity: 0.75; }

.datepicker-popup {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0;
  width: 280px;
  max-width: calc(100vw - 40px);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.45);
  padding: 14px;
}
.datepicker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.datepicker-month { font-weight: 700; font-size: 14px; color: var(--gold); text-transform: capitalize; }
.datepicker-nav {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--green);
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: 8px;
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.datepicker-nav:hover { background: var(--card-alt); }
.datepicker-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 4px;
}
.datepicker-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.datepicker-day {
  aspect-ratio: 1 / 1;
  border: none;
  background: transparent;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.datepicker-day:hover:not(.datepicker-day-empty) { background: var(--card-alt); }
.datepicker-day-empty { cursor: default; }
.datepicker-day-today { color: var(--green); font-weight: 800; }
.datepicker-day-selected { background: var(--green); color: #05070a; font-weight: 700; }
.datepicker-day-selected.datepicker-day-today { color: #05070a; }
.datepicker-footer {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.datepicker-footer-btn {
  background: transparent;
  border: none;
  color: var(--green);
  font-weight: 700;
  font-size: 12.5px;
  padding: 4px 6px;
}
.datepicker-footer-btn:hover { text-decoration: underline; }

/* --- Language switcher + language-detection prompt --- */
.lang-switcher {
  /* The generic `select { width: 100% }` rule above is written for
     full-width form selects (country pickers etc.) - without an explicit
     width here this pill inherits that 100% and stretches across the
     entire header instead of sitting as a small control next to the logo. */
  width: auto;
  flex-shrink: 0;
  max-width: 180px;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--card-alt);
  color: var(--ink);
  font-size: 13px;
  font-family: inherit;
}

.lang-prompt-banner {
  background: #241c0a;
  border: 1px solid #3d3013;
  color: var(--gold);
  border-radius: 12px;
  padding: 12px 16px;
  font-size: 13.5px;
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.lang-prompt-actions { display: flex; gap: 8px; flex-shrink: 0; }
.lang-prompt-actions button {
  padding: 7px 14px;
  font-size: 12.5px;
  border-radius: 999px;
}

/* Homepage "My Lists / Friends' Lists" dropdown (HANDOVER.md §25 follow-up) */
.lists-dropdown { position: relative; display: inline-block; }
.lists-dropdown-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  min-width: 240px;
  max-width: 320px;
  max-height: 360px;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  z-index: 20;
}
.lists-dropdown-section-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  padding: 8px 8px 4px;
}
.lists-dropdown-item, .lists-dropdown-link {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px;
  border-radius: 8px;
  color: var(--ink);
  text-decoration: none;
  font-size: 13.5px;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: inherit;
}
.lists-dropdown-item:hover, .lists-dropdown-link:hover { background: var(--card-alt); }
.lists-dropdown-empty { padding: 6px 8px 10px; font-size: 12.5px; color: var(--muted); }
.lists-dropdown-divider { height: 1px; background: var(--border); margin: 6px 0; }

/* Homepage landing content (added 12 August 2026) - the page used to jump
   straight from the header into the raw create-list form, which read like
   a plain utility/spreadsheet tool rather than a product with a point of
   view. This adds a proper landing sequence above the form: a punchier
   hero with a CTA, a relatable "here's the chaos you already know" before/
   after split, a 4-up feature grid, then the existing "How it works" list,
   then a CTA band that anchors down into the (unchanged) create-list form.
   Smooth-scrolls to #create so the hero CTA and the CTA band both land the
   visitor gently on the actual form rather than jumping instantly. */
html { scroll-behavior: smooth; }

/* Spacing bumped (13 Aug 2026, matching the intro-steps overlay's already-
   fixed version above): eyebrow now sits below the title here too (see the
   HTML), so it needs real breathing room on both sides to read as a distinct
   accent line rather than crowding the title above it or the sub-copy below
   it - margin-top set explicitly rather than relying on the <p> UA default. */
.eyebrow {
  color: var(--gold);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin: 18px 0 24px;
}
.hero2 { text-align: center; padding: 12px 0 56px; }
.hero2-title { font-size: clamp(28px, 5.5vw, 42px); margin: 0; }
.hero2-sub { max-width: 520px; margin: 0 auto 32px; font-size: 16px; }
.primary-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 16px;
  color: #05070a;
  background: linear-gradient(90deg, #25c6cb, #8cdfb4);
  text-decoration: none;
  border-radius: 999px;
  padding: 14px 30px;
}
.primary-cta:hover { filter: brightness(1.06); }

/* .relate* rules removed 15 Aug 2026 - the section they styled (a duplicate
   of intro-step-1's "You already know how this usually goes" comparison)
   was cut from index.html the same day; see the HTML comment there. */

.features {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin: 0 0 16px;
}
.feature {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 26px;
}
.feature-icon { display: inline-flex; width: 28px; height: 28px; margin-bottom: 14px; color: var(--green); }
.feature-icon svg { width: 100%; height: 100%; }
.feature h3 { font-size: 17.5px; margin: 0 0 10px; }
.feature p { font-size: 15px; line-height: 1.7; color: var(--muted); margin: 0; }

.cta-band {
  text-align: center;
  margin: 64px 0 28px;
  padding-top: 8px;
}
.cta-band h2 { font-size: clamp(22px, 4.5vw, 28px); margin: 0 0 10px; }

@media (max-width: 560px) {
  .features { grid-template-columns: 1fr; }
}

/* Background glow (added 12 August 2026) - two large, softly blurred brand-
   color blobs fixed behind everything, very low opacity, so the page reads
   as designed/premium rather than a flat solid-navy background. Fixed +
   negative z-index so it never scrolls or interferes with anything, and
   pointer-events:none so it can't intercept clicks. */
.bg-glow {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.bg-glow::before, .bg-glow::after {
  content: "";
  position: absolute;
  width: 60vmax;
  height: 60vmax;
  border-radius: 50%;
  filter: blur(110px);
  opacity: 0.16;
}
.bg-glow::before { background: #25c6cb; top: -22vmax; left: -18vmax; }
.bg-glow::after { background: #8cdfb4; bottom: -26vmax; right: -20vmax; }

/* Hero two-column layout + live claim-demo widget (added 12 August 2026) -
   the copy/CTA sits left, and a real-styled item card (same .item/.claim
   markup and CSS the actual owner page uses) sits right, looping on its
   own between "unclaimed" and "claimed" so visitors see the core mechanic
   in motion rather than just reading about it. Stacks to a single column
   on narrow screens, demo card centered below the copy. */
.hero-row {
  display: flex;
  align-items: center;
  gap: 48px;
  text-align: left;
}
.hero-copy { flex: 1 1 auto; min-width: 0; }
.hero-copy .hero2-sub { margin-left: 0; }
.hero-demo { flex: 0 0 260px; }
.demo-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  box-shadow: 0 24px 60px -20px rgba(37, 198, 203, 0.35);
}
.demo-card .item { border: none; }
.demo-card .thumb { font-size: 42px; }
.demo-caption {
  font-size: 12.5px;
  color: var(--muted);
  text-align: center;
  margin: 12px 0 0;
  min-height: 32px;
}
@media (max-width: 720px) {
  .hero-row { flex-direction: column; text-align: center; }
  .hero-copy .hero2-sub { margin-left: auto; margin-right: auto; }
  .hero-demo { flex: 0 0 auto; width: 100%; max-width: 260px; }
}

/* "See it in action" preview section (added 12 August 2026) - a real,
   accurately-styled 3-item grid (identical .grid/.item markup to the
   actual owner page) showing one claimed item hidden behind a disabled
   "✓ Claimed" pill, so a first-time visitor sees genuine product UI before
   ever creating a list, not just marketing copy about it. */
.preview { text-align: center; margin: 20px 0 64px; }
.preview-title { font-size: 21px; margin: 0 0 10px; }
.preview-sub { max-width: 480px; margin: 0 auto 28px; }
.preview-grid {
  text-align: left;
  /* Fixed 2 columns always (not auto-fit like the live app's .grid) - this
     section only ever holds exactly 4 static demo cards, and letting it
     auto-fit based on available width produced a 3+1 orphaned layout on
     wider screens. Deliberately different from the live app's grid, which
     needs to flex for real, variable-length item lists. */
  grid-template-columns: repeat(2, 1fr);
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}
.preview-grid .claim:disabled { cursor: default; }
/* Down to 2 cards now (17 Aug 2026, watch tile also removed per user
   feedback) - fills the fixed 2-column grid exactly, so the span/center
   trick this comment used to describe (added briefly for a 3-card version)
   is gone again - no orphan row to solve. */

/* "Search any real product" callout (added 12 August 2026) - the demo/
   preview cards above deliberately show generic, honest stock photography
   (see the item-naming fix in HANDOVER.md's §33 follow-ups - labeling a
   generic photo as a specific real brand would be misleading). This line
   makes clear that's just illustrative: the actual product-search feature
   (owner.html's "🔍 Search for a real product" card) pulls genuine branded
   results - real photo, real price, real store link - for whatever someone
   actually names. Styled as a small accent pill so it reads as a feature
   callout, not another paragraph of body copy. */
/* Changed from a pill/badge (border + tinted background) to plain accent
   text (13 Aug 2026, user feedback - the badge read as a UI chip dropped
   into the middle of a copy stack, not matching the title/subhead voice
   above it). Now a third line of copy, same family as .preview-sub, just
   colored to keep a little emphasis without breaking the flow. */
.preview-note {
  display: block;
  max-width: 480px;
  margin: 0 auto 32px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--green);
}

/* Scroll-reveal (added 12 August 2026) - see the matching IntersectionObserver
   in index.html. Elements start faded/lowered and animate up once scrolled
   into view; skipped for prefers-reduced-motion below. */
.reveal { opacity: 0; transform: translateY(18px); transition: opacity 600ms ease, transform 600ms ease; }
.reveal-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* Family/Group Lists (HANDOVER.md §24) - member chips (owner.html's
   "One list, but for more than one person" card), the grouped item-grid sections both
   owner.html and guest.html use once a list has members, and guest.html's
   "Who are you?" chooser. */
.members-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.member-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 6px 6px 12px;
  font-size: 13px;
  color: var(--ink);
}
.member-remove {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 50%;
}
.member-remove:hover { background: var(--border); color: var(--ink); }

.item-group { margin-top: 8px; }
.item-group-title {
  /* Bumped 12px -> 15px (17 Aug 2026, full-site audit following the
     homepage label-vs-content fix) - this section header (e.g. "Sarah",
     "Shared") sat above item cards whose own title (.item .title, 13.5px)
     and claim button (button, 15px) were both bigger than the header
     labeling them. Shared by owner.html and guest.html - verified both
     before changing. */
  font-size: 15px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 20px 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}
.item-group:first-child .item-group-title { margin-top: 0; }
.item-group-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 16px;
}
@media (max-width: 420px) {
  .item-group-grid { grid-template-columns: repeat(2, 1fr); }
}

.who-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.who-choice-active {
  background: var(--green);
  color: #06222a;
  border-color: var(--green);
  font-weight: 700;
}

/* Guest→owner growth loop - a low-key CTA card at the bottom of guest.html
   (styled like any other card, not an ad unit) plus a dismissible one-time
   nudge banner shown after a guest's first claim (see guest.html's
   maybeShowGrowthNudge()). */
.growth-cta { text-align: center; }
.growth-cta h2 { margin-bottom: 6px; }

.growth-nudge {
  position: fixed;
  /* Sits above the .toast pill (also bottom:20px) - a claim triggers both
     at once the one time this ever fires, so they'd otherwise overlap. */
  bottom: 70px;
  left: 50%;
  transform: translateX(-50%) translateY(12px);
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card-alt);
  border: 1px solid var(--border);
  color: var(--ink);
  padding: 10px 14px;
  border-radius: 999px;
  font-size: 13px;
  max-width: calc(100vw - 40px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 20;
}
.growth-nudge.show {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}
.growth-nudge-link {
  color: var(--green);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.growth-nudge-link:hover { text-decoration: underline; }
.growth-nudge-dismiss {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
}
.growth-nudge-dismiss:hover { color: var(--ink); }

/* Secret Santa (group gift exchange) - santa-organizer.html,
   santa-join.html, santa-reveal.html. Deliberately reuses the existing
   .card/.banner/.member-chip/button vocabulary rather than inventing a new
   visual language for one feature. */
.santa-participant-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.santa-hint { font-size: 12.5px; color: var(--muted); margin-top: 10px; }
.santa-private-link-box {
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
  margin-top: 14px;
}
.santa-drawn-banner {
  background: #0f2420;
  border: 1px solid #1e3d37;
  color: var(--gold);
  border-radius: 12px;
  padding: 16px;
  text-align: center;
  font-size: 15px;
}

/* Reveal page "flourish" - a gentle pop/settle rather than a full
   confetti simulation, in keeping with how restrained the rest of the
   app's motion already is (see .reveal/.reveal-visible and .splash's
   fade). Skipped under prefers-reduced-motion, same as .reveal below. */
@keyframes santaRevealPop {
  0% { opacity: 0; transform: scale(0.9) translateY(10px); }
  60% { opacity: 1; transform: scale(1.02) translateY(0); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.santa-reveal-pop { animation: santaRevealPop 550ms cubic-bezier(0.2, 0.8, 0.2, 1) both; }
@media (prefers-reduced-motion: reduce) {
  .santa-reveal-pop { animation: none; }
}

/* Secret Santa / Secret Gifting Group entry links under the create-list CTA
   on index.html (14 Aug 2026). Originally deliberately quieter than the
   primary "Create my list" button - making a gift list is the common case
   and must stay the obvious action, a gift exchange the occasional
   alternative.
   Made more prominent (17 Aug 2026, user feedback: "make the secret santa
   and secret grouping icons bigger so people see them now" - flagged with
   winter/Christmas approaching, when Secret Santa specifically becomes a
   much more common reason someone lands on this page). The 🎅/🎁 emoji are
   baked directly into the translated string itself across all 13 languages
   (see translations.js/.ts's santa.entrySanta/entryGeneric keys) rather
   than being separate elements, so scaling just the icon in isolation would
   mean restructuring every language's string - not worth it for a visual
   tweak. Scaled the whole button up instead (bigger font, more padding,
   a soft tinted background instead of just an outline) so icon+text both
   read larger and the buttons feel like real secondary actions rather than
   a quiet footnote link, without touching translation content at all. Still
   visually secondary to "Create my list" (smaller, outlined-then-tinted
   rather than solid/gradient) - just no longer easy to miss. */
/* .santa-entry (the old wrapper div - margin-top/padding-top/border-top to
   separate it from the form above it) removed (17 Aug 2026) now that this
   whole block is its own .card.card-highlight-soft.santa-card instead of
   living inside #startCard - the card's own padding/border does that job
   now, so the old divider rule was pure dead weight once the HTML div using
   it was deleted (see index.html's matching note). */
.santa-card { text-align: center; }
/* Bumped to match the same fix as .list-title-label above (17 Aug 2026,
   user feedback: "'Or set up a gift exchange instead' is again a header
   but its smaller than the rest of the box" - was 13.5px/muted while the
   Secret Santa/Secret Gifting Group buttons under it are 16px, same
   header-smaller-than-content issue as the main card's title label. */
.santa-entry-or {
  display: block;
  font-size: 17px;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 14px;
}
.santa-entry-links {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}
.santa-entry-links a {
  flex: 1 1 auto;
  /* Settled here after two rounds (17 Aug 2026): 10px/14px -> 14px/20px
     ("make the whole box a little bigger") -> 17px/24px ("a little bigger")
     -> back down to 13px/20px, same day, user feedback: "maybe only
     slightly a bit smaller so it doesn't confuse people as it takes away
     from the main box now" - 19px text was pulling visual weight away from
     "Create my list" above it, which needs to stay the clearly primary
     action. This lands roughly halfway between the original (13.5px/quiet)
     and the too-big version (19px/competing) - noticeably easier to spot
     than where this started, without outweighing the real CTA. */
  padding: 13px 20px;
  border: 1.5px solid var(--green);
  /* rgba values are --green (#25c6cb) manually converted to RGB (37, 198,
     203) - CSS can't reference a hex custom property with alpha added on
     top directly without color-mix(), which isn't used anywhere else in
     this file, so matching the literal RGB here keeps it exact rather than
     guessing a close-enough teal. */
  background: rgba(37, 198, 203, 0.1);
  border-radius: 999px;
  font-size: 16px;
  font-weight: 700;
  color: var(--green);
  text-decoration: none;
  white-space: nowrap;
}
.santa-entry-links a:hover { background: rgba(37, 198, 203, 0.2); }

/* The share card is the whole point of the product - a list nobody sees is
   just a note to yourself - but it used to sit as one more identical .card
   in a long scroll and disappeared entirely (user feedback, 14 Aug 2026).
   Given the app-wide "turquoise = you can act on this" rule, lifting it with
   the brand accent is the honest signal rather than mere decoration: a
   turquoise ring, a tinted wash, and a label promoted from small-grey to a
   proper heading. */
.share-card-featured {
  border-color: var(--green);
  box-shadow: 0 0 0 1px rgba(37, 198, 203, 0.35) inset,
              0 18px 44px -28px rgba(37, 198, 203, 0.5);
  background: linear-gradient(180deg, rgba(37, 198, 203, 0.07), rgba(37, 198, 203, 0) 70%), var(--card);
}
.share-card-featured label {
  color: var(--green);
  font-size: 15px;
  font-weight: 800;
  margin-bottom: 10px;
  display: block;
}
