/* ============================================================================
   site-chrome.css — the ONE stylesheet for shared site chrome: nav header,
   theme toggle, footer, the email-capture panel, and the button system those
   use. Linked by index.html, faq.html, benchmark.html (and any future page).

   CASCADE CONTRACT: this sheet is linked in <head> BEFORE each page's own
   inline <style>. So a page's surviving inline rule always wins a tie — this
   sheet can only FILL IN chrome rules a page no longer defines, never clobber
   a page-specific override. That ordering is what makes the de-duplication
   safe.

   Design tokens (:root / [data-theme]) deliberately stay in each page's inline
   <style> — CSS custom properties resolve by inheritance at use-time, not by
   stylesheet source order, so the var(--…) references below resolve fine
   against whatever each page defines. Values here mirror index.html (the
   canonical page); minor pre-existing drift on the sub-pages (nav bg opacity,
   nav max-width) is intentionally unified to index's values.
   ============================================================================ */

/* ========= NAV (sticky, frosted) =========
   Default = sub-page behavior: sticky + always visible (faq, benchmark, future
   pages with no hero). index.html owns a hero brand mark (#siteHeader); the
   `body:has(#siteHeader)` block below upgrades it to the fixed,
   hidden-until-scroll-past-hero nav. */
.site-nav {
  position: sticky; top: 0; z-index: 50;
  background: rgba(251,250,247,0.78);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-bottom: 1px solid var(--border-subtle);
}
[data-theme="dark"] .site-nav { background: rgba(12,13,15,0.78); }

/* index.html only: nav hides until the visitor scrolls past the hero logo.
   The IntersectionObserver in site-chrome.js toggles `.is-visible`.
   SAFARI: a fixed, backdrop-filtered element that's translated off-screen
   leaves a ghost blur band pinned to the top of the viewport (WebKit
   backdrop-root artifact; Chrome unaffected). So while hidden the nav drops
   its blur/paint entirely and goes visibility:hidden (delayed past the hide
   transition; instant on reveal). */
body:has(#siteHeader) .site-nav {
  position: fixed; left: 0; right: 0;
  transform: translateY(-110%);
  opacity: 0;
  visibility: hidden;
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: transform 280ms cubic-bezier(0.2, 0, 0.2, 1), opacity 220ms ease, visibility 0s linear 280ms;
}
body:has(#siteHeader) .site-nav.is-visible {
  transform: translateY(0); opacity: 1; visibility: visible;
  background: rgba(251,250,247,0.78);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  transition: transform 280ms cubic-bezier(0.2, 0, 0.2, 1), opacity 220ms ease, visibility 0s;
}
[data-theme="dark"] body:has(#siteHeader) .site-nav { background: transparent; }
[data-theme="dark"] body:has(#siteHeader) .site-nav.is-visible { background: rgba(12,13,15,0.78); }

.nav-inner {
  display: flex; justify-content: space-between; align-items: center;
  max-width: 1200px; margin: 0 auto;
  padding: 0.75rem 1.5rem;
  gap: 1.5rem;
}
.nav-brand {
  display: flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-display); font-size: 1.25rem; font-weight: 700;
  text-decoration: none; color: var(--text-primary);
  letter-spacing: -0.015em;
}
.nav-brand .nav-icon {
  width: 40px; height: 40px;
  flex-shrink: 0;
  display: block;
  /* Inline gradient SVG mark; the shadow is ours to tune (soft + light, follows
     the squircle) rather than the pinched shadow baked into the old PNG. */
  filter: drop-shadow(0 2px 4px rgba(20, 34, 60, 0.18));
}
.nav-links { display: flex; align-items: center; gap: 0.4rem; }
.nav-links a {
  position: relative;
  font-size: 0.875rem; font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 150ms ease, background 140ms ease, box-shadow 140ms ease, transform 110ms ease;
}
.nav-links a:hover, .nav-links a.current { color: var(--text-primary); }
.nav-links a.current { font-weight: 600; }

/* Nav state system — "Lift & Fill". THREE distinct states on the frosted bar,
   for the TOP-LEVEL text links AND the Overview dropdown button:
     hover   = soft neutral chip (you're about to click)
     pressed = darker chip + push-in (tactile)
     active  = raised WHITE pill w/ hairline border + shadow (current page)
   Neutral for interaction, a lit white pill for "you are here" — so a hover
   never gets confused with the current page. `.nav-links > a` (direct children)
   + .nav-drop-btn scopes it to the top-level items; the dropdown MENU items keep
   their own plain colour/background hover (below). The chip padding is absorbed
   by a tighter .nav-links gap so the header rhythm stays ~unchanged. */
.nav-links > a, .nav-drop-btn { padding: 5px 12px; border-radius: 9px; }
.nav-links > a:hover, .nav-drop-btn:hover, .nav-drop.open .nav-drop-btn {
  background: color-mix(in srgb, var(--text-primary) 9%, transparent);
}
.nav-links > a:active, .nav-drop-btn:active {
  background: color-mix(in srgb, var(--text-primary) 18%, transparent);
  transform: scale(0.96); box-shadow: inset 0 1px 3px rgba(0,0,0,0.14);
}
.nav-links > a.current {
  background: var(--bg-surface, #fff);
  box-shadow: 0 1px 2px rgba(14,20,30,0.16), 0 4px 12px rgba(14,20,30,0.10),
              inset 0 0 0 1px var(--border-default);
}
/* Dark bar: a white pill would glare, so the current page reads as a soft raised
   light chip instead. */
[data-theme="dark"] .nav-links > a.current {
  background: color-mix(in srgb, #fff 10%, transparent);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}
.nav-links > a:focus-visible, .nav-drop-btn:focus-visible {
  outline: none; color: var(--text-primary);
  background: color-mix(in srgb, var(--text-primary) 9%, transparent);
}

/* ---- Product dropdown — the ONE nested nav item (home-page sections).
   Click-toggles via site-chrome.js (adds .open + aria-expanded) and also
   hover-opens on pointer devices; the ::after bridge covers the 10px gap so
   the pointer can travel button → menu without the hover chain breaking. ---- */
/* inline-flex so the wrapper hugs the button (height 22.4) instead of
   imposing its own 16px/25.6px block line box, which baseline-dropped the
   button ~0.9px below the sibling anchors. */
.nav-drop { position: relative; display: inline-flex; align-items: center; }
.nav-drop-btn {
  display: inline-flex; align-items: center; gap: 0.3rem;
  background: none; border: 0; padding: 5px 12px; cursor: pointer;
  font-family: var(--font-body); font-size: 0.875rem; font-weight: 500;
  /* Match .nav-links a line-height (inherited 1.6). Buttons get UA
     line-height:normal, which made the taller anchors' text sit ~0.5px
     lower than "Overview" — this equalizes the text baseline. */
  line-height: 1.6;
  color: var(--text-secondary);
  transition: color 150ms ease, background 140ms ease, box-shadow 140ms ease, transform 110ms ease;
}
.nav-drop-btn:hover, .nav-drop.open .nav-drop-btn { color: var(--text-primary); }
.nav-drop-btn svg { width: 11px; height: 11px; margin-top: 1px; transition: transform 150ms ease; }
.nav-drop.open .nav-drop-btn svg { transform: rotate(180deg); }
.nav-drop-menu {
  position: absolute; top: calc(100% + 10px); left: -14px; min-width: 230px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md, 12px);
  box-shadow: var(--shadow-float);
  padding: 0.4rem; display: none; z-index: 60;
}
[data-theme="light"] .nav-drop-menu { border-color: rgba(100,89,43,0.105); box-shadow: var(--shadow-float), 0 0.5px 0 rgba(100,89,43,0.135); }
.nav-drop.open .nav-drop-menu { display: block; }
/* Open state is owned ENTIRELY by site-chrome.js (.open class): hover-intent
   with a close grace, plus click toggle. No CSS :hover opening — the earlier
   dual hover/click state made the menu feel glitchy to interact with. The
   ::after strip below bridges the 10px visual gap so the pointer can travel
   button → menu without a mouseleave firing in between. */
.nav-drop.open::after { content: ""; position: absolute; top: 100%; left: 0; right: 0; height: 12px; }
.nav-drop-menu a {
  display: block; padding: 0.5rem 0.75rem; border-radius: 8px;
  font-size: 0.875rem; font-weight: 500; color: var(--text-secondary);
  text-decoration: none; transition: color 120ms ease, background 120ms ease;
}
.nav-drop-menu a:hover { background: var(--bg-surface-muted); color: var(--text-primary); }

.nav-cta { position: relative; display: flex; align-items: center; gap: 0.875rem; }
.nav-cta .ghost { position: relative; font-size: 0.875rem; font-weight: 600; color: var(--color-primary-base); text-decoration: none; transition: color 150ms ease; }
.nav-cta .ghost:hover { color: var(--color-primary-hover); }
.nav-cta .ghost::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -5px; height: 1.5px;
  border-radius: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: center; opacity: 0;
  transition: transform 170ms cubic-bezier(.4,0,.2,1), opacity 150ms ease;
}
.nav-cta .ghost:hover::after, .nav-cta .ghost:focus-visible::after { transform: scaleX(1); opacity: 1; }
.nav-cta .ghost:active { opacity: 0.7; }
.nav-cta .ghost:focus-visible { outline: none; }
/* Dual-tier nav pills (surface both plans up top) */
.nav-plan .np-unit { font-weight: 500; opacity: 0.85; font-size: 0.92em; margin-left: 0.15rem; }
/* ---- Mobile hamburger + drawer ----
   Desktop: burger hidden, .nav-links shown, drawer never displayed.
   ≤760px: .nav-links + the price pills + Download ghost hide, burger appears,
   and #navMobile drops down as a full-width drawer holding every destination.
   (Prior to this, ≤760px hid .nav-links with nothing in its place — phones had
   no navigation at all.) */
.nav-burger { display: none; }
.nav-mobile { display: none; }
@media (max-width: 760px) {
  .nav-links { display: none; }
  /* Declutter the top bar on phones: brand + burger only. The cryptic
     $$ once / $$$ /yr price pills and the Download ghost read as noise at
     phone width — they move INTO the drawer (Pricing + Download Cover). */
  .nav-cta { display: none; }

  .nav-burger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; margin-left: auto;
    background: none; border: 0; padding: 0; cursor: pointer;
    border-radius: 10px; color: var(--text-primary);
    -webkit-tap-highlight-color: transparent;
    transition: background 140ms ease;
  }
  .nav-burger:active { background: color-mix(in srgb, var(--text-primary) 12%, transparent); }
  .nav-burger-box { position: relative; width: 20px; height: 14px; display: block; }
  .nav-burger-box span {
    position: absolute; left: 0; width: 100%; height: 2px; border-radius: 2px;
    background: currentColor;
    transition: transform 220ms cubic-bezier(.4,0,.2,1), opacity 160ms ease, top 220ms cubic-bezier(.4,0,.2,1);
  }
  .nav-burger-box span:nth-child(1) { top: 0; }
  .nav-burger-box span:nth-child(2) { top: 6px; }
  .nav-burger-box span:nth-child(3) { top: 12px; }
  /* Burger → X when open */
  .nav-burger[aria-expanded="true"] .nav-burger-box span:nth-child(1) { top: 6px; transform: rotate(45deg); }
  .nav-burger[aria-expanded="true"] .nav-burger-box span:nth-child(2) { opacity: 0; }
  .nav-burger[aria-expanded="true"] .nav-burger-box span:nth-child(3) { top: 6px; transform: rotate(-45deg); }

  .nav-mobile {
    display: block; position: absolute; top: 100%; left: 0; right: 0;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-float);
    /* Collapsed by default; .open animates it in. */
    max-height: 0; overflow: hidden;
    transition: max-height 260ms cubic-bezier(.4,0,.2,1);
  }
  .nav-mobile.open { max-height: 80vh; overflow-y: auto; }
  .nav-mobile-inner {
    display: flex; flex-direction: column; gap: 2px;
    padding: 0.5rem 0.75rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    max-width: var(--content-max, 1200px); margin: 0 auto;
  }
  .nav-mobile-label {
    font: 600 0.6875rem/1 var(--font-body); letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--text-secondary);
    padding: 0.75rem 0.75rem 0.35rem;
  }
  .nav-mobile a {
    display: block; padding: 0.75rem 0.75rem; border-radius: 10px;
    font-size: 1rem; font-weight: 500; color: var(--text-primary);
    text-decoration: none; transition: background 120ms ease;
  }
  .nav-mobile a:active { background: var(--bg-surface-muted); }
  .nav-mobile .nav-mobile-dl {
    margin-top: 0.5rem; text-align: center; font-weight: 600;
    color: #fff; background: var(--color-primary-base);
  }
  .nav-mobile .nav-mobile-dl:active { background: var(--color-primary-hover); }
}
@media (prefers-reduced-motion: reduce) {
  .nav-mobile, .nav-burger-box span { transition: none; }
}

/* ========= THEME TOGGLE ========= */
.theme-toggle {
  position: fixed; bottom: 1.25rem; right: 1.25rem; z-index: 100;
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  background: var(--bg-surface-raised);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: 999px;
  font-family: var(--font-body); font-size: 0.75rem; font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-float);
  transition: transform 180ms ease, border-color 180ms ease;
}
.theme-toggle:hover { transform: translateY(-1px); border-color: var(--color-primary-base); }

/* ========= BUTTON SYSTEM (used by nav CTAs + email-capture) ========= */
.btn { display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-md); font-family: var(--font-body); font-weight: 600; cursor: pointer; transition: background 150ms ease, transform 150ms ease, box-shadow 150ms ease, border-color 150ms ease; border: 0; text-decoration: none; }
.btn-primary { background: var(--color-primary-base); color: var(--color-on-primary, #fff); padding: 14px 28px; font-size: 0.9375rem; }
[data-theme="light"] .btn-primary { box-shadow: 0 1px 2px rgba(74,143,243,0.22), 0 2px 8px rgba(74,143,243,0.18); }
[data-theme="dark"] .btn-primary { box-shadow: 0 1px 3px rgba(0,0,0,0.4); }
/* Text on the primary fill must survive a page-level generic `a:hover`
   (specificity 0,1,1, loaded AFTER this sheet) that used to repaint the pill
   text blue-on-blue → invisible. The doubled class (.btn.btn-primary) out-
   specifies it in EVERY interaction state. `--color-on-primary` /
   `--color-primary-pressed` were never defined anywhere (dark text on blue,
   no press feedback) — the fallbacks fix that site-wide. */
.btn.btn-primary,
.btn.btn-primary:link, .btn.btn-primary:visited,
.btn.btn-primary:hover, .btn.btn-primary:focus,
.btn.btn-primary:focus-visible, .btn.btn-primary:active {
  color: var(--color-on-primary, #fff);
}
.btn-primary:hover { background: var(--color-primary-hover); transform: translateY(-1px); }
[data-theme="light"] .btn-primary:hover { box-shadow: 0 2px 5px rgba(74,143,243,0.28), 0 8px 18px rgba(74,143,243,0.22); }
.btn-primary:active { background: var(--color-primary-pressed, #2f66bd); transform: translateY(0); box-shadow: 0 1px 2px rgba(74,143,243,0.20); }
.btn-primary:focus-visible { outline: 2px solid var(--color-primary-base); outline-offset: 2px; }
.btn-secondary { background: var(--bg-surface); color: var(--text-primary); border: 1px solid var(--border-strong); padding: 14px 26px; font-size: 0.9375rem; box-shadow: var(--shadow-float); }
[data-theme="dark"] .btn-secondary { background: transparent; border-color: rgba(255,255,255,0.16); }
.btn-secondary:hover { border-color: var(--color-primary-base); }
.btn-pill-sm { padding: 0.5rem 0.875rem; font-size: 0.8125rem; border-radius: var(--radius-md); }
.btn-outline { background: transparent; color: var(--text-primary); border: 1px solid var(--border-strong); }
[data-theme="dark"] .btn-outline { border-color: rgba(255,255,255,0.18); }
/* Doubled class so `a:hover` can't override the outline text tint. */
.btn.btn-outline:hover {
  border-color: var(--color-primary-base); color: var(--color-primary-base);
  background: color-mix(in srgb, var(--color-primary-base) 8%, transparent);
  transform: translateY(-1px);
}
.btn.btn-outline:active {
  color: var(--color-primary-base); transform: translateY(0);
  background: color-mix(in srgb, var(--color-primary-base) 15%, transparent);
}
.btn-outline:focus-visible { outline: 2px solid var(--color-primary-base); outline-offset: 2px; }
/* Disabled Buy button — visible price, no action, until purchasing is live. */
.btn-disabled,
.btn-disabled:hover {
  background: var(--bg-surface-muted);
  color: var(--text-muted);
  border: 1px solid var(--border-subtle);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}

/* ========= INLINE EMAIL CAPTURE (slide-open, no modal) =========
   Gates the free download behind an email. Uses a max-height reveal (child has
   overflow:hidden). On the nav it anchors as a small panel under the CTA. */
.email-capture {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  transition: max-height 340ms cubic-bezier(0.4,0,0.2,1),
              opacity 220ms ease, margin-top 340ms cubic-bezier(0.4,0,0.2,1);
}
/* max-height is a safe cap above the tallest panel (form or done state), not
   the exact height — overflow:hidden clips the slack while closed. */
.email-capture.is-open { max-height: 400px; opacity: 1; margin-top: 0.75rem; }
.email-capture:not(.is-open) { pointer-events: none; }
/* When an inline download panel is open (form OR the post-submit download
   state — is-open stays set through both), its trigger button is replaced by
   the panel, so hide it. Pure CSS keyed on the live panel state, so the button
   can never re-appear stacked above the form/download (the nav dropdown floats,
   so it's excluded). */
.intro-card:has(.email-capture.is-open) [data-download-trigger],
.card-pricing:has(.email-capture.is-open) [data-download-trigger] { display: none; }
/* Nav "Download" opens a small panel anchored under the nav CTA. */
.email-capture.ec-nav {
  position: absolute; top: calc(100% + 12px); right: 0;
  width: min(340px, 78vw);
  background: var(--bg-surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  z-index: 1000;
}
.email-capture.ec-nav.is-open { margin-top: 0; }
.email-capture.ec-nav .ec-inner { padding: 1rem 1.125rem 1.125rem; }
.ec-prompt { font-size: 0.8125rem; line-height: 1.45; color: var(--text-secondary); margin: 0 0 0.625rem; }
.ec-row { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.ec-input {
  flex: 1 1 160px; min-width: 0;
  padding: 13px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 0.9375rem;
  transition: border-color 150ms ease, box-shadow 150ms ease;
  -webkit-appearance: none; appearance: none;
}
[data-theme="dark"] .ec-input { background: var(--bg-surface-muted); }
.ec-input::placeholder { color: var(--text-muted); }
.ec-input:focus { outline: none; border-color: var(--color-primary-base); box-shadow: 0 0 0 3px rgba(74,143,243,0.18); }
.email-capture.is-error .ec-input { border-color: var(--status-critical); box-shadow: 0 0 0 3px rgba(229,72,77,0.16); }
.ec-submit { flex: 1 1 120px; white-space: nowrap; padding: 13px 18px; }
.ec-submit[disabled] { opacity: 0.6; cursor: default; }
.ec-msg { font-size: 0.75rem; line-height: 1.45; margin: 0.5rem 0 0; min-height: 1em; }
/* When there's no status text, the row reserves no space — otherwise an empty
   status line left a sloppy gap below the macOS-requirements caption in every
   panel. It re-expands (and is announced) only when a message is set. */
.ec-msg:empty { min-height: 0; margin-top: 0; }
.ec-msg.is-err { color: var(--status-critical); }
.ec-msg.is-ok { color: var(--status-success); }
/* Success state: the form is replaced in place by a download button. */
.ec-done { display: none; }
.email-capture.is-done .ec-form { display: none; }
.email-capture.is-done .ec-done { display: block; }
.ec-download { width: 100%; }
.ec-done-note { font-size: 0.75rem; line-height: 1.45; color: var(--text-muted); text-align: center; margin: 0.5rem 0 0; }
.ec-done-note::before { content: "✓ "; color: var(--status-success); font-weight: 700; }
/* System-requirements caption (shared with the pricing CTAs on index). */
.sysreq { font-family: var(--font-body); color: var(--text-muted); font-size: 0.72rem; letter-spacing: 0.03em; text-align: center; margin: 0.625rem 0 0; }
@media (prefers-reduced-motion: reduce) {
  .email-capture { transition: opacity 120ms ease; }
  .email-capture.is-open { max-height: 400px; }
}

/* ========= FOOTER ========= */
footer {
  padding: 3.5rem 1.5rem 2.5rem;
  background: var(--bg-base);
  border-top: 1px solid var(--border-subtle);
  text-align: left;
}
/* 1152px = the header's inner content width (nav-inner max-width 1200 − 2×24px
   padding), so the footer content sits on the SAME left/right rail as the header
   — the flat footer mark lines up vertically under the header logo. */
footer .container { max-width: 1152px; margin: 0 auto; }
/* ── Link columns — Overview (home-page anchors) + Product (standalone pages).
   Each column: an uppercase eyebrow head over a stack of links. ── */
/* Footer top row: flat brand mark in its own left column, then the link
   columns. The mark is a solid-fill SVG build of the app icon (no gradient or
   shadow) — see partials/footer.html. */
footer .footer-top {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  /* row gap 2.5rem; column gap 3rem brings the link columns nearer the small
     brand mark (the 5rem link-to-link spacing stays on .footer-cols).
     cb: 2026-07-15c — fresh hash to dodge a poisoned edge-cache entry. */
  gap: 2.5rem 3rem;
  margin: 0 0 2.5rem;
}
/* Brand mark doubles as a "back to top" control (wired in site-chrome.js). */
footer .footer-brand { flex-shrink: 0; display: inline-block; cursor: pointer; }
footer .footer-mark { display: block; width: 36px; height: 36px; transition: transform 160ms ease; }
footer .footer-brand:hover .footer-mark { transform: translateY(-2px); }
footer .footer-brand:active .footer-mark { transform: translateY(0); }
footer .footer-cols {
  display: flex; flex-wrap: wrap; gap: 2.5rem 5rem;
}
footer .footer-col { display: flex; flex-direction: column; gap: 0.625rem; }
footer .footer-col-head {
  margin: 0 0 0.25rem;
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-muted);
}
footer .footer-col a {
  color: var(--text-secondary);
  font-size: 0.875rem; line-height: 1.2;
  text-decoration: none;
  transition: color 150ms ease;
}
footer .footer-col a:hover { color: var(--color-primary-base); }
/* ── Base bar — copyright + small legal links on the left, dogcow on the right,
   separated from the columns by a hairline. ── */
footer .footer-base {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border-subtle);
}
footer .footer-base-left { display: flex; flex-direction: column; gap: 0.5rem; }
footer .footer-legal { display: flex; gap: 1.25rem; flex-wrap: wrap; }
footer .footer-legal a {
  color: var(--text-muted);
  font-size: 0.75rem;
  text-decoration: none;
  transition: color 150ms ease;
}
footer .footer-legal a:hover { color: var(--color-primary-base); }
footer .copyright {
  color: var(--text-muted);
  font-size: 0.8125rem;
  margin: 0;
}
/* Dogcow sits at the right of the base bar; the Moof badge floats out to the
   LEFT on hover (so it stays inside the page instead of overflowing the edge). */
footer .dogcow-easter {
  display: inline-flex; align-items: center; justify-content: center;
  position: relative;
  opacity: 0.55;
  transition: opacity 240ms ease;
  flex-shrink: 0;
}
footer .dogcow-easter:hover { opacity: 1; }
footer .dogcow {
  width: 40px; height: auto;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
}
footer .moof {
  position: absolute;
  right: calc(100% + 12px);
  top: 50%;
  padding: 4px 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: 99px;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-primary);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-50%) translateX(6px);
  transition: opacity 200ms ease, transform 200ms ease;
}
footer .dogcow-easter:hover .moof { opacity: 1; transform: translateY(-50%) translateX(0); }
