/* topnav.css — canonical rules for the site topnav.
   Loaded by every page (index.html, harmony.html, the SSR year page)
   so the bar is pixel-identical everywhere. Edit ONLY this file when
   tweaking topnav appearance. */

/* Cross-document View Transitions opt-in. The CSS at-rule (NOT the
   <meta name="view-transition"> tag, which only governs same-document
   transitions) is what the spec requires for cross-doc nav. With both
   the source and destination page declaring this, Chromium 126+ keeps
   the old tab state visible until the new document is paint-ready —
   suppressing the "globe → favicon" flicker that the user otherwise
   sees on every / ↔ /year ↔ /harmony nav. Lives in topnav.css so it
   applies to all three pages without duplicating the rule. */
@view-transition {
  navigation: auto;
}

/* Font fallbacks so pre-Google-Fonts metrics match across pages and
   the bar height doesn't jump while web fonts load. */
@font-face { font-family: 'DM Sans Fallback'; src: local('Arial'); size-adjust: 100.06%; ascent-override: 92.5%; descent-override: 24.1%; line-gap-override: 0%; }
@font-face { font-family: 'Lora Fallback'; src: local('Georgia'); size-adjust: 107.4%; ascent-override: 93.5%; descent-override: 25.65%; line-gap-override: 0%; }

/* position:fixed (not sticky) so the topnav stays anchored to the
   viewport edge and can NOT bounce with the document during
   browser-elastic overscroll on macOS / iOS. Sticky elements
   visually move with the page during the rubber-band, which on
   /year (with its second subnav) caused gaps and detachments —
   and on every page made the bar feel slightly "loose" at the
   extremes of scroll. Body padding-top below compensates for the
   58px of layout space sticky was reserving (fixed is out of flow). */
.topnav { position: fixed; top: 0; z-index: 100; background: #ffffff; border-bottom: 1px solid var(--border); transition: box-shadow 0.22s ease; width: 100%; }
body { padding-top: 58px; }
/* Anchor jumps (e.g. tab navigation on /name/<slug>) must land
   below the fixed topnav, not behind it. scroll-padding-top on the
   scrolling element shifts the anchor target down by the topnav
   height. */
html { scroll-padding-top: 58px; }
/* Tighter shadow than the original (was 0 6px 20px / 26px reach).
   The previous large blur extended 26px into body content, and with a
   fixed topnav the area under it had to be recomposited on every
   scroll frame — visibly fuzzing scrolling text. The smaller shadow
   keeps the visual lift while shrinking the recomposite zone to ~6px. */
.topnav.scrolled { box-shadow: 0 2px 4px rgba(20,20,20,0.08); }
/* Pre-paint shadow on year-page reloads — applied via inline <head>
   script that reads sessionStorage so the shadow is present at first
   paint with no transition flicker. */
html.nan-preset-scrolled .topnav { box-shadow: 0 2px 4px rgba(20,20,20,0.08); transition: none; }
.topnav-inner { max-width: 1100px; margin: 0 auto; padding: 10px 2rem; display: flex; align-items: center; gap: 16px; min-height: 58px; }

.topnav-logo { display: flex; align-items: center; gap: 8px; text-decoration: none; color: var(--ink); flex-shrink: 0; min-width: 0; position: relative; top: -2px; }
.topnav-logo:hover, .topnav-logo:hover .topnav-logo-text { color: var(--ink); text-decoration: none; }
.topnav-logo-icon-stage { position: relative; width: 30px; height: 30px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; }
.topnav-logo-icon { width: 30px; height: 30px; display: block; flex-shrink: 0; }
.topnav-logo-text { font-family: 'Lora', 'Lora Fallback', serif; font-size: 16px; font-weight: 600; white-space: nowrap; position: relative; top: 2px; display: inline-block; color: var(--ink); line-height: 1; vertical-align: baseline; }
.topnav-logo-text em { color: var(--blue); font-style: italic; }
.topnav-logo-not { position: relative; display: inline-block; line-height: 1; }
.topnav-logo-name { line-height: 1; }

/* Desktop layout (and mobile via the @media override below):
   logo on the left, then a flex gap, then the search bar, then the
   Baby names / Names by year / More tools pills aligned to the right
   edge. flex order: logo(1) → search(2) → links(3). The search's
   `margin-left: auto` consumes leading whitespace so search + links
   sit as one right-aligned group. */
.topnav-links { display: flex; align-items: center; gap: 16px; flex-shrink: 0; order: 3; }
.topnav-links .topnav-explore-wrap { display: inline-flex; }
/* Blue→pink palette across all nav pills. Same family of colours as the
   "Is your baby due this century?" CTA on /year, scaled up to a full
   design language for the topnav.

   Layering: each pill stacks two backgrounds — the inner one clipped to
   padding-box (the fill) and the outer one clipped to border-box (the
   "border"). The real border is `1px solid transparent`, letting the
   outer gradient show through as a coloured rim that respects the
   pill's border-radius (a regular gradient `border-image` would square
   off the corners). All three states share this structure, swapping
   only the colour values:

     state    inner fill                       outer "border"
     ─────────────────────────────────────────────────────────────
     default  near-white blue→pink wash        same, low-opacity
     hover    full --blue-light → --pink-light same, mid-opacity + spin
     active   full --blue-light → --pink-light saturated --blue → --pink

   The gradient angle is a registered custom property (`--nav-angle`)
   so we can animate it as a real angle — a plain CSS animation can't
   interpolate gradient stops directly. On hover, the angle rotates a
   full turn (135deg → 495deg), spinning both the fill and the border
   in unison so the swirl reads on this small pill. Active stays static
   so a constantly-rotating "current page" pill never becomes a
   distraction.

   --amber-light is intentionally still defined elsewhere for future
   use, per the brief. */
@property --nav-angle {
  syntax: '<angle>';
  initial-value: 135deg;
  inherits: false;
}
.topnav-link {
  font-family: 'DM Sans', 'DM Sans Fallback', system-ui, sans-serif;
  font-size: 14px; font-weight: 500;
  color: var(--ink); text-decoration: none;
  /* Asymmetric vertical padding (9/7) shifts the text 1px down so it
     sits at the optical centre of the pill. DM Sans's cap-height/baseline
     ratio places glyphs slightly above the geometric centre of the line
     box; even 1px of correction is visible at this pill size. */
  padding: 9px 18px 7px; border-radius: 50px;
  background:
    linear-gradient(var(--nav-angle), #F6FAFD 0%, #FEF8FB 100%) padding-box,
    linear-gradient(var(--nav-angle), rgba(74,144,191,0.22) 0%, rgba(194,84,122,0.22) 100%) border-box;
  border: 1px solid transparent;
  transition: background 0.25s ease, color 0.18s, --nav-angle 0.4s ease;
  white-space: nowrap; cursor: pointer;
  line-height: 1.2;
}
/* Hover only changes the inner fill from near-white wash to the full
   blue-light/pink-light gradient; the border stays at the same 22%
   opacity used at rest, so the "outline" doesn't visibly thicken on
   hover-in. Border darkening is reserved for the active state. */
.topnav-link:hover,
.topnav-explore-btn:hover {
  background:
    linear-gradient(var(--nav-angle), var(--blue-light) 0%, var(--pink-light) 100%) padding-box,
    linear-gradient(var(--nav-angle), rgba(74,144,191,0.22) 0%, rgba(194,84,122,0.22) 100%) border-box;
  color: var(--ink); text-decoration: none;
  animation: navPillSpin 3.5s linear infinite;
}
/* Active rim: --blue/--pink at 75% opacity, a step down from full
   saturation so the rim reads "selected" without competing for the
   eye with the cream page background. */
.topnav-link.active,
.topnav-link.active:hover,
.topnav-explore-btn[aria-expanded="true"],
.topnav-explore-btn.active,
.topnav-explore-btn.active:hover {
  background:
    linear-gradient(var(--nav-angle), var(--blue-light) 0%, var(--pink-light) 100%) padding-box,
    linear-gradient(var(--nav-angle), rgba(74,144,191,0.75) 0%, rgba(194,84,122,0.75) 100%) border-box;
  border: 1px solid transparent;
  color: var(--ink);
  animation: none;
}
@keyframes navPillSpin {
  to { --nav-angle: 495deg; }
}
@media (prefers-reduced-motion: reduce) {
  .topnav-link:hover,
  .topnav-explore-btn:hover { animation: none; }
}

/* Footer pills. Two variants:
   - .footer-pill (default): white background, neutral border. Used for
     soft prompts that aren't feature cross-links (e.g. "Did we help
     you decide…").
   - .footer-pill--feature: blue→pink wash matching the topnav-link
     default fill, so feature cross-links visually echo the topnav
     "Baby names / Names by year / More tools" pills. */
.footer-pill {
  background: white;
  border: 1px solid var(--border);
  border-radius: 50px;
  /* Asymmetric vertical padding (9/7) shifts the text 1px down so it
     sits at the optical centre of the pill — mirrors .topnav-link. DM
     Sans's cap-height/baseline ratio places glyphs slightly above the
     geometric centre of the line box. */
  padding: 9px 16px 7px;
  font-family: 'DM Sans', 'DM Sans Fallback', sans-serif;
  font-size: 13px;
  color: var(--ink);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: border-color 0.18s, background 0.18s;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.2;
}
.footer-pill:hover { border-color: var(--ink); }
.footer-pill--feature {
  background:
    linear-gradient(135deg, #F6FAFD 0%, #FEF8FB 100%) padding-box,
    linear-gradient(135deg, rgba(74,144,191,0.22) 0%, rgba(194,84,122,0.22) 100%) border-box;
  border: 1px solid transparent;
}
.footer-pill--feature:hover {
  background:
    linear-gradient(135deg, var(--blue-light) 0%, var(--pink-light) 100%) padding-box,
    linear-gradient(135deg, rgba(74,144,191,0.22) 0%, rgba(194,84,122,0.22) 100%) border-box;
  border: 1px solid transparent;
}

.topnav-explore-wrap { position: relative; }
.topnav-explore-btn {
  padding: 9px 18px 7px; border-radius: 50px;
  background:
    linear-gradient(var(--nav-angle), #F6FAFD 0%, #FEF8FB 100%) padding-box,
    linear-gradient(var(--nav-angle), rgba(74,144,191,0.22) 0%, rgba(194,84,122,0.22) 100%) border-box;
  color: var(--ink);
  border: 1px solid transparent;
  font-family: 'DM Sans', 'DM Sans Fallback', system-ui, sans-serif;
  font-size: 14px; font-weight: 500;
  cursor: pointer; transition: background 0.25s ease, color 0.18s, --nav-angle 0.4s ease;
  white-space: nowrap; display: inline-flex; align-items: center; gap: 6px;
  line-height: 1.2;
}
/* Caret drawn with CSS borders so it stays crisp at any font scale and
   stays vertically centred with the text. The character glyph used to
   stretch in some Lora/DM Sans weights. */
.topnav-explore-caret { display: inline-block; width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 6px solid currentColor; transition: transform 0.18s; position: relative; top: 1px; font-size: 0; }
.topnav-explore-btn[aria-expanded="true"] .topnav-explore-caret { transform: rotate(180deg); }

/* Animated hamburger: three lines that fold into an X when the menu is open.
   Built from spans so we can transition them; a unicode glyph can't animate.
   Sized at 14×10 so it sits comfortably inside the 30px round mobile pill
   (matching the logo icon's diameter). */
.topnav-hamburger { display: inline-block; width: 14px; height: 10px; position: relative; vertical-align: middle; }
.topnav-hamburger span { position: absolute; left: 0; right: 0; height: 2px; background: currentColor; border-radius: 1px; transition: transform 0.22s ease, opacity 0.18s ease, top 0.22s ease; }
.topnav-hamburger span:nth-child(1) { top: 0; }
.topnav-hamburger span:nth-child(2) { top: 4px; }
.topnav-hamburger span:nth-child(3) { top: 8px; }
.topnav-explore-btn[aria-expanded="true"] .topnav-hamburger span:nth-child(1) { top: 4px; transform: rotate(45deg); }
.topnav-explore-btn[aria-expanded="true"] .topnav-hamburger span:nth-child(2) { opacity: 0; }
.topnav-explore-btn[aria-expanded="true"] .topnav-hamburger span:nth-child(3) { top: 4px; transform: rotate(-45deg); }

.topnav-explore-menu {
  position: absolute; top: calc(100% + 8px); right: 0;
  background: white; border: 1px solid var(--border);
  border-radius: 14px; box-shadow: 0 12px 36px rgba(44,44,42,0.10);
  width: 300px; padding: 6px;
  display: none; z-index: 200;
}
.topnav-explore-menu.open { display: block; }
.topnav-explore-menu a, .topnav-explore-menu button {
  display: block; width: 100%; text-align: left;
  padding: 12px 14px; border-radius: 10px;
  background: transparent; border: 0;
  color: var(--ink); text-decoration: none; cursor: pointer;
  font-family: inherit;
}
/* 6px breathing room between stacked menu items so a hover-tint never
   visually touches the rim of the active (current-page) item. 2px was
   too tight — the hover-bg still grazed the amber inset rim of its
   neighbour at common scroll-bar pixel snapping. */
.topnav-explore-menu > * + * { margin-top: 6px; }
.topnav-explore-menu a:hover, .topnav-explore-menu button:hover {
  background: linear-gradient(135deg, var(--blue-light) 0%, var(--pink-light) 100%);
  text-decoration: none;
}
/* Dropdown items have no native border, so the active rim is faked with
   an inset box-shadow. Switched from amber to a soft --blue tint so the
   "current page" signal stays in the same palette as the topnav pills —
   no more yellow rim on mobile, where the dropdown is the primary nav. */
.topnav-explore-current,
.topnav-explore-current:hover {
  background: linear-gradient(135deg, var(--blue-light) 0%, var(--pink-light) 100%) !important;
  box-shadow: inset 0 0 0 1px rgba(74,144,191,0.45);
}
.topnav-explore-menu strong {
  display: block;
  font-family: 'Lora', 'Lora Fallback', serif;
  font-size: 16px; font-weight: 600; color: var(--ink);
}
.topnav-explore-menu span {
  display: block; color: var(--muted);
  font-size: 13px; margin-top: 2px; line-height: 1.35;
}

/* Visible state intentionally omits `transform: translateY(0)` — an
   identity transform still promotes the element onto its own GPU
   compositor layer, which on macOS can shift text rendering away
   from subpixel-positioning. The transition still works because CSS
   interpolates from `translateY(-4px)` to `none` smoothly. */
.topnav-search { position: relative; width: 300px; margin-left: auto; flex-shrink: 0; order: 2; opacity: 0; transform: translateY(-4px); pointer-events: none; transition: opacity 0.22s ease, transform 0.22s ease; }
.topnav-search.visible, .topnav-search--always { opacity: 1; transform: none; pointer-events: auto; }
.topnav-search input {
  width: 100%; height: 36px; line-height: 36px;
  padding: 0 36px 0 18px;
  border: 1px solid var(--border); border-radius: 50px;
  font-family: 'DM Sans', 'DM Sans Fallback', system-ui, sans-serif; font-size: 14px;
  background: white; color: var(--ink);
  outline: none; box-sizing: border-box; text-overflow: ellipsis;
}
.topnav-search input:focus { border-color: var(--border); }
.topnav-search input::placeholder { color: var(--muted); text-overflow: ellipsis; overflow: hidden; }
.topnav-search-clear {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 22px; height: 22px;
  display: none; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--cream); border: none;
  cursor: pointer; color: var(--muted);
  font-family: Arial, sans-serif;
  line-height: 22px; padding: 0; font-size: 14px;
}
.topnav-search-clear:hover { background: var(--border); color: var(--ink); }
.topnav-search-clear.visible { display: inline-flex; }

/* Responsive — applied identically across all three pages. Asymmetric
   top/bottom padding preserved at every breakpoint (top one larger
   than bottom) so the 1px down-shift compensating DM Sans's
   cap-height/baseline asymmetry is consistent on desktop and mobile.
   Lose this and text sits a perceptible 1px too high in the pill. */
@media (max-width: 1100px) {
  .topnav-link { padding: 8px 14px 6px; font-size: 13px; }
  .topnav-explore-btn { padding: 8px 14px 6px; font-size: 13px; }
  /* gap only — no margin-left. With the order flipped (search 2,
     links 3) extra margin-left on links would push them away from
     the search pill instead of keeping them adjacent on the right. */
  .topnav-links { gap: 6px; }
}
@media (max-width: 1000px) {
  .topnav-search { width: 240px; }
}
@media (max-width: 860px) {
  .topnav-link { padding: 8px 13px 6px; font-size: 13px; }
  .topnav-explore-btn { padding: 8px 12px 6px; font-size: 13px; }
  .topnav-inner { gap: 8px; padding: 10px 1rem; }
  .topnav-links { gap: 6px; }
  .topnav-search { width: 200px; }
}
@media (max-width: 720px) {
  .topnav-inner { padding: 8px 1rem; gap: 8px; }
  .topnav-search { width: 170px; }
}
@media (max-width: 640px) {
  /* On mobile, the dropdown anchors the right edge and the search bar sits
     to its left. Flex `order` is used to flip source order without
     touching the HTML, keeping the markup identical across pages. The
     standalone Baby names / Names by year pills hide; their entries
     resurface inside the dropdown (.topnav-explore-menu-mobile). */
  .topnav-links > a.topnav-link { display: none; }
  /* The search bar (order 2) has margin-left/right:auto, so even when
     collapsed to max-width:0 its auto margins still consume free
     space and push the hamburger to the right edge. */
  .topnav-links { order: 3; margin-left: 0; gap: 0; }
  /* Search is centred between logo and hamburger via balanced auto
     margins. Width transitions from 0 (collapsed) to 225 (expanded) so
     the layout doesn't reflow on scroll — the bar grows in place
     while the logo wordmark slides off. Default state is collapsed
     because the 225px wide invisible slot would otherwise push the
     hamburger past the right edge of a 375px viewport. */
  .topnav-search {
    order: 2; margin-left: auto; margin-right: auto;
    width: 225px; max-width: 0; overflow: hidden;
    transition: max-width 0.28s ease, opacity 0.22s ease, transform 0.22s ease;
  }
  .topnav-search.visible, .topnav-search--always { max-width: 225px; }
  /* iOS Safari zooms the page on input focus when font-size < 16px.
     Bump the search input to 16px on mobile so the viewport stays put
     when the keyboard opens — the placeholder still fits at 225px. */
  .topnav-search input { font-size: 16px; }
  /* Round hamburger pill sized to match the logo's 30px icon body — so
     the two visually balance at either end of the nav bar (round logo
     face on the left, round menu pill on the right). */
  .topnav-explore-btn { padding: 0; gap: 0; width: 30px; min-width: 30px; height: 30px; border-radius: 50%; justify-content: center; align-items: center; }
  /* Default mobile: show the full wordmark ("Not another Noah") so
     brand is visible on first paint. When the search input becomes
     visible (either via scroll on /, which toggles
     .topnav-search.visible, or by virtue of --always on /year and
     /harmony), the wordmark slides off to make room — see the
     :has() rule below. */
  .topnav-logo-text {
    display: inline-block;
    /* identity transform omitted — see .topnav-search rule above for
       why (avoids needless GPU layer promotion that fuzzes text). */
    max-width: 200px; opacity: 1; transform: none;
    overflow: hidden; white-space: nowrap;
    transition: max-width 0.28s ease, opacity 0.2s ease, transform 0.28s ease;
  }
  .topnav-inner:has(.topnav-search.visible) .topnav-logo-text,
  .topnav-inner:has(.topnav-search--always) .topnav-logo-text {
    max-width: 0; opacity: 0; transform: translateX(-10px);
  }
  /* "Noah moment": when the user searches Noah, the logo gets
     .noah-moment and the scribble animates under "Not". On mobile that
     animation only lands if the full wordmark is visible, so the logo
     wins the real estate over the search bar while the moment is live. */
  .topnav-inner:has(.topnav-logo.noah-moment) .topnav-logo-text,
  .topnav-inner:has(.topnav-logo.noah-exit) .topnav-logo-text {
    /* identity transform omitted — see .topnav-search rule above for
       why (avoids needless GPU layer promotion that fuzzes text). */
    max-width: 200px; opacity: 1; transform: none;
    /* Let the scribble paint below the baseline of "Not". The slide-off
       rule above clips it; we explicitly unclip while the moment is live. */
    overflow: visible;
  }
  .topnav-inner:has(.topnav-logo.noah-moment) .topnav-search,
  .topnav-inner:has(.topnav-logo.noah-exit) .topnav-search {
    /* Collapse to 0 width (rather than display:none) so the layout
       transitions in/out instead of snapping. The search's own auto
       margins still anchor the hamburger to the right edge. */
    max-width: 0;
  }
  .topnav-inner { padding: 8px 0.75rem; gap: 8px; }
  /* Dropdown anchored to viewport edge so it never clips off-screen. */
  .topnav-explore-menu { width: calc(100vw - 1.5rem); max-width: 320px; right: -0.25rem; }
}

/* Label swap on the dropdown button: "More tools" on desktop, animated
   hamburger on mobile so the only nav button doesn't read as boring as
   "Menu". */
.topnav-explore-btn-label-mobile { display: none; }
@media (max-width: 640px) {
  .topnav-explore-btn-label-desktop { display: none; }
  .topnav-explore-btn-label-mobile { display: inline-flex; line-height: 1; }
  /* Caret is redundant when the hamburger itself animates to an X. */
  .topnav-explore-caret { display: none; }
}

/* Floating shortlist button — same shape, position, and behaviour on
   every page that loads topnav.css. Visibility (count + .visible) is
   driven by JS on each page; the styling here is shared so the FAB
   never "jumps" between pages. Mirrors the main site's #shortlistFab
   in styles.css; defined here too so /harmony and /year/* pick it up
   without pulling in app.js. */
.shortlist-fab {
  position: fixed; bottom: 24px; right: 24px; z-index: 900;
  display: none; align-items: center; gap: 6px;
  padding: 12px 18px; border-radius: 50px;
  background: linear-gradient(135deg, #B8943E 0%, #D4B05A 50%, #B8943E 100%);
  color: white; border: none; text-shadow: 0 1px 1px rgba(0,0,0,0.1);
  font-family: 'DM Sans', 'DM Sans Fallback', sans-serif; font-size: 14px; font-weight: 600;
  cursor: pointer; box-shadow: 0 4px 16px rgba(0,0,0,0.15);
  transition: transform 0.2s, box-shadow 0.2s; white-space: nowrap;
  text-decoration: none;
  /* Named-element View Transition: when navigating between /year/N
     pages (or any same-origin nav with view-transition: same-origin),
     the browser pairs old + new FAB and morphs the single element
     instead of crossfading the whole region, so the FAB doesn't blink
     off→on across the load. */
  view-transition-name: shortlist-fab;
}
.shortlist-fab.visible,
/* Pre-paint hint: a <head> inline script on /year (and similar pages)
   reads localStorage and toggles .nan-has-shortlist on <html> BEFORE
   first paint. Without this, the FAB stays display:none until the
   deferred sync script runs after parse — visibly flashing on every
   /year → /year nav under View Transitions. */
html.nan-has-shortlist .shortlist-fab { display: inline-flex; }
.shortlist-fab:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.2); text-decoration: none; color: white; }
.shortlist-fab svg { width: 18px; height: 18px; }
.shortlist-fab .fab-heart-icon { display: inline-flex; align-items: center; position: relative; top: 1px; }
.shortlist-fab .fab-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; border-radius: 10px;
  background: white; color: #B8943E; font-size: 12px; font-weight: 700;
  padding: 0 5px;
}
@media (max-width: 600px) {
  .shortlist-fab { bottom: 16px; right: 16px; padding: 10px 14px; font-size: 13px; }
}

/* Heart animations shared across pages. Originally only in
   styles.css (main site); promoted here so the FAB pulse + floating
   hearts also play when /harmony's in-card shortlist button writes
   a save. Mirrors the .heart-btn add experience on /name pages. */
.shortlist-fab.fab-added { animation: fabBtnPulse 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes fabBtnPulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}
.fab-float-heart {
  position: fixed; pointer-events: none; z-index: 901;
  color: #B8943E; opacity: 0;
  animation: floatHeartUp var(--dur) ease-out forwards;
  animation-delay: var(--delay);
}
@keyframes floatHeartUp {
  0%   { opacity: 0; transform: translate(0, 0) scale(0.5); }
  8%   { opacity: 0.7; }
  20%  { opacity: 0.6; transform: translate(var(--drift1), -12px) scale(0.85); }
  50%  { opacity: 0.4; transform: translate(var(--drift2), -35px) scale(0.75); }
  80%  { opacity: 0.12; transform: translate(var(--drift3), -58px) scale(0.6); }
  100% { opacity: 0; transform: translate(var(--drift3), -70px) scale(0.5); }
}
@keyframes heartPop {
  0% { transform: scale(1); }
  50% { transform: scale(1.25); }
  100% { transform: scale(1); }
}


/* Mobile-only entries that surface the Baby names / Names by year links
   inside the dropdown when the standalone pills are hidden. Selectors
   include `.topnav-explore-menu` so they beat the menu-anchor default
   `display: block` rule on specificity. */
.topnav-explore-menu .topnav-explore-menu-mobile { display: none; }
@media (max-width: 640px) {
  .topnav-explore-menu .topnav-explore-menu-mobile { display: block; }
}
