/* public/css/chrome.css — Phase 2 of the refactor (REFACTOR_PLAN.md §7).
   Copied verbatim from public/dashboard-v2.css ranges:
     218..612   (reset / focus / layout shell)
     840..871   (button library)
     1431..2229 (mobile shell + topbar + bottom-tab nav)
*/

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

/* 2026-05-22: html bg explicit so the safe-area-inset region below the
   builder chat input doesn't show a white seam between the cream body
   and the iOS Safari URL chrome. Without this, html defaults to white
   and any reveal (rubber-band, safe-area, content > viewport) flashes
   white. Matches the body bg one-to-one. */
html { background-color: var(--cream); }

/* 2026-05-22 belt-and-braces: in builder-chat mode every ancestor
   that could render below the chat input gets explicit cream bg.
   Without this, the safe-area-inset-bottom padding on the chat-pane
   showed white on iOS Safari because the inherited transparent
   chain ended at an element whose computed bg wasn't being applied
   (cache, parse-cascade, or iOS-specific safe-area painting behaviour
   — fixing each layer makes any of those root causes go away). */
body.is-builder-chat,
body.is-builder-chat .shell,
body.is-builder-chat .shell > main.dash,
body.is-builder-chat .builder-shell,
body.is-builder-chat .builder-shell--centered,
body.is-builder-chat .builder-chat-pane,
body.is-builder-chat .builder-input-wrap {
  /* Builder page bg = --topband so the WHOLE AI builder surface reads
     as one continuous warm-cream panel matching the sidebar + topbar +
     composer. (Was --cream / #FAFAF8 earlier — different shade from the
     sidebar #FDFDFD, founder flagged that mismatch 2026-05-22.) */
  background-color: var(--topband) !important;
}
/* Ensure [hidden] beats display: grid/flex/inline-flex set on .side-id, .metrics, .body-grid, etc. */
[hidden] { display: none !important; }
body {
  font-family: 'Geist', 'Inter', system-ui, sans-serif;
  background-color: var(--cream);
  background-image: none;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Hanging punctuation on display headings */
.dash-greeting h1, .pg-title, .rich-title, blockquote, .pull-quote {
  hanging-punctuation: first allow-end last;
}

/* Variable font features (Geist stylistic alternates) */
.dash-greeting h1, .pg-title, .rich-title, .display, .metric-value,
.site-info h2, .rec-hero h2 {
  font-feature-settings: "ss01" 1, "ss02" 1, "cv11" 1, "cv01" 1;
  font-optical-sizing: auto;
}

/* Frosted decorative divider utility */
.frosted-rule {
  border: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--hairline-2) 18%, var(--hairline-2) 82%, transparent);
  margin: var(--space-6) 0;
}

/* === Smart focus rings (adaptive) ========================================= */
:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
  border-radius: 6px;
}
.btn:focus-visible { outline-offset: 3px; }
/* Dark surfaces (sidebar active link, primary button) get warm cream halo */
.side-link.is-active:focus-visible,
.btn-primary:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px oklch(96.5% 0.018 80 / 0.4), inset 0 1px 0 rgba(255,255,255,0.06);
}

/* === Layout shell ========================================================== */
.shell {
  display: grid;
  grid-template-columns: 264px 1fr;
  min-height: 100vh;
}

/* === Sidebar =============================================================== */
/* Chrome-less: sidebar bg uses --topband so chrome → topbar → sidebar
   reads as one continuous surface (mobile drawer blend) and the desktop
   sidebar reads as a subtly warmer panel next to the neutral page. */
.side {
  background: var(--topband);
  border-right: 1px solid rgba(0, 0, 0, 0.06);
  height: 100vh;
  position: fixed; top: 0; left: 0;
  width: 264px;
  display: flex; flex-direction: column;
  overflow: hidden;
  z-index: 30;
  grid-column: 1;
  grid-row: 1;
}
/* Sidebar is fixed → out of grid flow. Pin .dash + sub-page wrappers
   to column 2 explicitly so they don't fall back into column 1. */
.shell > main.dash,
.shell > .pg,
.shell > .rich-page { grid-column: 2; }
[data-theme="dark"] .side {
  border-right-color: rgba(255, 255, 255, 0.12);
}
.side-link:hover { background: var(--cream-3); }
.side-link.is-active { background: var(--cream-3); }

/* Sidebar collapse toggle (inside sidebar header) */
.side-brand { justify-content: flex-start; }
.side-toggle {
  margin-left: auto;
  background: transparent;
  border: 0;
  padding: 5px;
  border-radius: 6px;
  color: var(--ink-mute);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.18s cubic-bezier(0.2, 0, 0, 1), color 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.side-toggle:hover { background: var(--cream-3); color: var(--ink); }
.side-toggle svg { display: block; }

/* Floating "open sidebar" button — visible in AI builder first-run state.
   Lovable-style flat disc: soft ambient shadow + faint ring, no convex
   gradient, no rim-light. Lovable's button is nearly flat — the weight
   comes from the size + clean borderless edge, not heavy shadow. */
.side-opener {
  display: none;
  align-items: center;
  justify-content: center;
  position: fixed;
  top: 14px; left: 14px;
  z-index: 60;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.04),
    0 1px 2px rgba(0, 0, 0, 0.04),
    0 4px 12px rgba(0, 0, 0, 0.06);
  transition: transform 0.18s var(--ease-out, cubic-bezier(0.2, 0, 0, 1)),
              box-shadow 0.18s var(--ease-out, cubic-bezier(0.2, 0, 0, 1));
}
.side-opener:hover {
  transform: translateY(-1px);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.06),
    0 2px 4px rgba(0, 0, 0, 0.06),
    0 8px 20px rgba(0, 0, 0, 0.08);
}
.side-opener:active {
  transform: translateY(0);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.06),
    0 1px 2px rgba(0, 0, 0, 0.04);
}
.side-opener svg { display: block; }
/* Collapsed sidebar = narrow icon rail (Lovable-style). The sidebar stays
   visible at 64px, labels hide, icons center. side-opener stays out of
   the way since the rail is its own affordance. */
body.is-side-collapsed .shell,
html.is-side-collapsed-pre body .shell { grid-template-columns: 64px 1fr; }
body.is-side-collapsed .side,
html.is-side-collapsed-pre body .side { width: 64px; }
body.is-side-collapsed .side-opener,
html.is-side-collapsed-pre body .side-opener { display: none; }

/* Hide text + show icons centered. Brand wordmark, link labels,
   kbd hints, upgrade-card body text, and account name all collapse;
   icons + diamond logo + avatar circle stay visible. */
body.is-side-collapsed .side-brand-name,
body.is-side-collapsed .side-link > span:not(.bs-logo-mark):not([class*="logo"]),
body.is-side-collapsed .side-link-kbd,
body.is-side-collapsed .side-foot-upgrade__title,
body.is-side-collapsed .side-foot-upgrade__cta,
body.is-side-collapsed .side-foot-name { display: none !important; }

body.is-side-collapsed .side-brand {
  flex-direction: column;
  gap: 8px;
  padding: 14px 8px;
  align-items: center;
}
body.is-side-collapsed .side-toggle { margin-left: 0; }
body.is-side-collapsed .side-link {
  justify-content: center;
  padding: 10px 8px;
  gap: 0;
}
body.is-side-collapsed .side-nav-section { margin: 12px 14px; }
body.is-side-collapsed .side-foot-upgrade {
  padding: 8px;
  display: flex;
  justify-content: center;
  /* Show a small icon stand-in instead of the lost text. */
  min-height: 32px;
}
body.is-side-collapsed .side-foot-upgrade::before {
  content: '';
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  background: var(--accent-orange, #D57529);
  opacity: 0.85;
}
body.is-side-collapsed .side-foot {
  padding: 12px 8px;
  display: flex;
  justify-content: center;
}
body.is-side-collapsed .side-foot-avatar { margin: 0; }

/* Single small logo top-left. No surrounding card. */
.side-brand {
  display: flex; align-items: center; gap: 8px;
  padding: 18px 18px 14px;
}
.side-brand-name {
  font-family: 'Geist', 'Inter Tight', 'Inter', sans-serif;
  font-weight: 500;
  font-size: 14px;
  color: var(--ink);
  white-space: nowrap;
}

.side-nav {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 6px 10px 10px;
  scrollbar-width: thin;
  display: flex; flex-direction: column;
}

/* Visual gap between groups (replaces the old mono-caps section heads) */
.side-nav-gap {
  height: 14px;
}
/* Cleaner section break — thin hairline + breathing room. Used in the
   6-item sidebar to separate site / daily-work / settings sections. */
.side-nav-section {
  height: 1px;
  background: var(--hairline);
  margin: 12px 8px;
}

/* Search row — looks like a side-link but acts as a button (opens Cmd+K).
   Reset native button defaults so it sits flush with anchor side-links. */
.side-link--search {
  background: transparent;
  border: 0;
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.side-link-kbd {
  margin-left: auto;
  font-family: 'Geist', 'Inter', sans-serif;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  background: var(--cream-2);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 2px 6px;
  line-height: 1;
  font-weight: 500;
}
.side-link:hover .side-link-kbd { color: var(--ink-mute); }

.side-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 7px 10px;
  border-radius: 6px;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink-mute);
  cursor: pointer;
  text-align: left;
  text-decoration: none;
  transition: background 0.18s cubic-bezier(0.2, 0, 0, 1), color 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.side-link > svg {
  flex-shrink: 0;
  color: var(--ink-faint);
  transition: color 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.side-link:hover > svg,
.side-link.is-active > svg {
  color: var(--ink);
}
.side-link.is-dim > svg { color: var(--ink-faint); }
a.side-link { color: var(--ink-mute); }
a.side-link:visited { color: var(--ink-mute); }
.side-link:hover { background: var(--cream-2); color: var(--ink); }
.side-link.is-active {
  background: var(--cream-2);
  color: var(--ink);
  font-weight: 500;
}
/* Hub-style sidebar item: chevron pushed to the right edge so the
   item reads as "tap to open this group". Used on Site / Bookings /
   Settings parent items in the consolidated 6-item sidebar. */
.side-link--hub > span:not(.side-link__chev) { flex: 1 1 auto; }
.side-link__chev {
  margin-left: auto;
  color: var(--ink-faint);
  font-size: 14px;
  line-height: 1;
  transition: color 0.18s cubic-bezier(0.2, 0, 0, 1), transform 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.side-link--hub:hover .side-link__chev,
.side-link--hub.is-active .side-link__chev { color: var(--ink); transform: translateX(2px); }
.side-link.is-dim { color: var(--ink-faint); }
.side-link.is-dim:hover { color: var(--ink); }

.side-foot {
  border-top: 0;
  padding: 14px 12px;
  display: flex; align-items: center; gap: 10px;
}
.side-foot-avatar {
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--cream-3);
  color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 500; font-size: 12px;
  flex: 0 0 auto;
}
.side-foot-name {
  font-size: 14px;
  color: var(--ink-mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Sidebar upgrade nudge — sits above the avatar row, Builder tier only.
   Placeholder href #bump-up per CLAUDE.md (don't wire to /billing yet). */
.side-foot-upgrade {
  display: block;
  margin: 8px 12px 0;
  padding: 12px 14px;
  background: var(--cream-3);
  border-radius: 10px;
  text-decoration: none;
  transition: background 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.side-foot-upgrade:hover { background: var(--cream-2); }
.side-foot-upgrade__title {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.3;
  margin-bottom: 4px;
}
.side-foot-upgrade__cta {
  font-size: 12px;
  color: var(--orange);
  font-weight: 500;
}
@media (max-width: 899px) { .side-foot-upgrade { display: none; } }

/* BS logo mark */
.bs-logo-mark {
  display: inline-block;
  width: 18px; height: 18px;
  flex-shrink: 0;
  line-height: 0;
}
.bs-logo-mark svg { width: 100%; height: 100%; display: block; }
.bs-logo-mark__d {
  fill: var(--ink);
  transform-origin: center;
  transition: opacity 0.25s ease;
}
/* The hero diamond rosette on home page keeps the orange brand mark. */
.home-mark-svg .bs-logo-mark__d,
.home-mark-svg.is-brand .bs-logo-mark__d { fill: var(--orange); }
/* ──── chrome.css range 2: source lines 840..871 ──── */
/* === Buttons ============================================================== */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 14px;
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 500;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 0.18s cubic-bezier(0.2, 0, 0, 1), border-color 0.18s cubic-bezier(0.2, 0, 0, 1), color 0.18s cubic-bezier(0.2, 0, 0, 1);
  text-decoration: none;
  color: var(--ink);
  white-space: nowrap;
}
.btn svg { width: 13px; height: 13px; }
.btn-ghost { background: transparent; border-color: var(--hairline-2); }
.btn-ghost:hover { background: var(--cream-2); border-color: var(--hairline-3); }
.btn-primary {
  background: var(--orange);
  color: #fff;
  border-color: var(--orange);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.btn-primary:hover {
  background: var(--orange-deep);
  border-color: var(--orange-deep);
}
.btn-primary:active {
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.18);
}

/* ──── chrome.css range 3: source lines 1431..2229 ──── */
/* ==========================================================================
   Mobile shell — top bar + side drawer (visible only at <900px).
   Moved from dashboard-v2-page.html 2026-05-02 so dashboard-v2.html (the
   shell that serves /dashboard-v2 root) also gets the hamburger.
   ========================================================================== */
.mob-topbar { display: none; }
.mob-drawer-backdrop { display: none; }
.mob-drawer-close { display: none; }


/* ──── chrome.css patch: dashboard-v2.css lines 1381..1430 (mobile shell
    collapse — initially MISSED from the Phase 2 split, causing the sidebar
    to render at full width on mobile because chrome.css's redeclaration of
    the base .shell rule was overriding dashboard-v2.css's mobile override). ──── */
@media (max-width: 899.98px) {
  :root {
    --card-pad: var(--card-pad-tight);
  }

  /* Shell: single column. Drawer is positioned: fixed (set in the page's
     inline style block) so it sits outside the flow. */
  .shell {
    grid-template-columns: 1fr;
    min-height: auto;
  }
  /* Override the desktop `grid-column: 2` on the main content wrappers —
     at <900px shell collapses to one column, but the explicit
     `grid-column: 2` was creating an implicit second track that
     content-sized .dash and shoved everything against the right edge
     (visible at 768px tablet portrait). Pin to column 1 instead. */
  .shell > main.dash,
  .shell > .pg,
  .shell > .rich-page { grid-column: 1; }
  /* Restore side-brand text and foot text inside the drawer at this size
     (the 880px rule above hides them for the icon-rail mode, which we
     don't use on phones). */
  .side-brand-name,
  .side-foot-name { display: inline; }
  .side-link { padding: 9px 12px; font-size: 14px; }

  /* All multi-column layout grids stack on phones. */
  .body-grid { grid-template-columns: 1fr; }
  .site-card { grid-template-columns: 1fr; }
  .rec-hero { grid-template-columns: 1fr; }
  .connect-card { grid-template-columns: 1fr; }
  .metrics,
  .metrics.is-five { grid-template-columns: repeat(2, 1fr); }
  .quick { grid-template-columns: repeat(2, 1fr); }
  .upsell-stat { grid-template-columns: 1fr 1fr; }

  /* Hero/list trim. */
  .rec-hero-stats { flex-wrap: wrap; gap: 14px; }
  .dash-greeting { flex-direction: column; align-items: flex-start; gap: 4px; }
  .foot { flex-direction: column; gap: 6px; align-items: flex-start; }

  /* Padding tightens; the per-page inline style block also handles .dash. */
  .dash { padding: 16px 16px 32px; }
  .pg, .rich-page { padding: 16px 16px 32px; }

  /* Hide the desktop floating opener at this size — the topbar burger
     is the canonical entry point on mobile. */
  .side-opener { display: none !important; }
}


@media (max-width: 899.98px) {
  /* Stable mobile frame.
     - position:fixed (not sticky) so the topbar never detaches when the
       body or any ancestor changes its overflow / scroll container.
     - .shell pushed down by exactly the topbar height so content doesn't
       slip under it.
     - body uses 100dvh so the iOS address-bar shrink doesn't shake the
       layout, plus overscroll-behavior:none kills rubber-band that makes
       the topbar visually "jump" on iOS.
     - Body scroll lock when the drawer is open uses position:fixed
       (the standard iOS pattern) instead of overflow:hidden, which on
       iOS Safari does NOT actually lock body scroll. */
  html, body {
    min-height: 100vh;
    min-height: 100dvh;
    overscroll-behavior: none;
  }
  body { position: relative; }
  /* ──────────────────────────────────────────────────────────────────
     SHARED .mob-topbar — used on BOTH home (dashboard-v2.html) AND
     AI builder (dashboard-v2-page.html). Convention to prevent bleed:
       - Base rule (here) = visual contract shared across both pages.
       - Home-only tweaks → body.is-empty-dash .mob-topbar { ... }
       - Builder-only tweaks → body.is-builder-chat .mob-topbar { ... }
     Never set bg/colour here for a builder-only reason — it bleeds
     to the home topbar. (Has happened: cream vs topband, see commit
     09a26ba for the revert.)
     ────────────────────────────────────────────────────────────── */
  .mob-topbar {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 52px;
    padding: 0 12px;
    /* Topbar bg uses --topband to match the sidebar exactly. Default
       across every page (home, leads, recovery, settings, etc.). The
       AI builder page overrides this to transparent + the glass-blur
       stack — scoped to body.is-builder-chat below so this default
       is preserved on every other surface that shares the stylesheet. */
    background: var(--topband);
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 70;
    will-change: transform;
  }
  /* 2026-05-24 — Progressive glass blur (E2 from /_demo-glass) is
     builder-only. body.is-builder-chat lands on the AI builder page;
     other pages keep the solid --topband background above. The three
     .mob-topbar-glass children only exist in dashboard-v2-page.html
     so they wouldn't render elsewhere even if the rules matched, but
     scoping the transparent + overflow:visible bits to .is-builder-
     chat keeps the home/leads/recovery topbars untouched. */
  body.is-builder-chat .mob-topbar {
    background: transparent;
    overflow: visible;
  }
  /* 2026-05-24 — Progressive glass blur, Lovable-style: NO tint
     background on any layer, only backdrop-filter. When nothing
     scrolls under the topbar the layers are completely invisible
     (the cream background shows through unchanged) — when content
     does scroll under, the blur kicks in. The previous version had
     white-tint gradients (0.12 / 0.18 / 0.24 alpha) which painted a
     visible band even over uniform bg, making the topbar look like
     a separate panel. */
  body.is-builder-chat .mob-topbar-glass {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    z-index: -1;
    pointer-events: none;
  }
  /* 2026-05-24 — gentler ramp. Previous values jumped 4 → 14 → 32 px
     blur with masks that stayed solid for the first 55% of each
     layer's height — so the top 55% of the cumulative effect was
     "all layers at full strength", which read as harsh/abrupt. Now:
     blur values are closer (3 → 8 → 16) AND every mask fades from
     y=0, no flat region. The heaviest blur is only fully visible at
     the very top edge and tapers immediately. Cumulative effect at
     the top is softer + the gradient down to crisp content is
     smoother. */
  /* 2026-05-24 — pure blur, no saturate(). Founder report: topbar
     still has a different colour from the background even with no
     content behind it. saturate() was amplifying the cream's faint
     warm tone, leaving a slightly warmer band where each filter
     was active. blur() of a uniform colour returns that same
     colour exactly — invisible over the background — so dropping
     saturate is the only way to make the topbar genuinely
     disappear when nothing's scrolling behind it. */
  /* 2026-05-24 — heavier blur up top + flat hold region before each
     layer fades. Founder: 'more intense blur earlier on, then hold
     it for a bit and let it disappear.' Each mask is solid for the
     first 60% of its height (the 'hold') then tapers to transparent
     by 100% (the 'disappear'). Cumulative result: heaviest glass at
     the top, a steady plateau through the chrome zone, then a
     clean fade-out by ~50px. */
  body.is-builder-chat .mob-topbar-glass[data-layer="1"] {
    height: 70px;
    -webkit-backdrop-filter: blur(1px);
            backdrop-filter: blur(1px);
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
  }
  body.is-builder-chat .mob-topbar-glass[data-layer="2"] {
    height: 70px;
    -webkit-backdrop-filter: blur(2px);
            backdrop-filter: blur(2px);
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
  }
  body.is-builder-chat .mob-topbar-glass[data-layer="3"] {
    height: 70px;
    -webkit-backdrop-filter: blur(3px);
            backdrop-filter: blur(3px);
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 0%, #000 60%, transparent 100%);
  }
  /* Push everything below the fixed topbar so content doesn't slide under. */
  .shell { padding-top: 52px; }
  /* Lovable-style flat disc — matches .side-opener on desktop.
     Soft ambient shadow + faint ring, no convex gradient. */
  .mob-topbar-burger {
    flex: 0 0 auto;
    width: 48px; height: 48px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid rgba(31, 22, 17, 0.13);
    border-bottom-color: rgba(31, 22, 17, 0.19);
    border-radius: 50%;
    padding: 0;
    color: var(--ink);
    cursor: pointer;
    background: linear-gradient(180deg, #FFFFFF 0%, #F8F8F8 100%);
    box-shadow:
      0 1px 3px rgba(31, 22, 17, 0.07),
      0 2px 1px -1px rgba(31, 22, 17, 0.04),
      inset 0 1px 0 rgba(255, 255, 255, 0.9);
    transition: all 0.15s ease;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    position: relative;
    z-index: 71;
  }
  .mob-topbar-burger:hover {
    border-color: rgba(31, 22, 17, 0.2);
    border-bottom-color: rgba(31, 22, 17, 0.26);
    box-shadow:
      0 2px 5px rgba(31, 22, 17, 0.09),
      0 2px 1px -1px rgba(31, 22, 17, 0.04),
      inset 0 1px 0 rgba(255, 255, 255, 0.9);
  }
  .mob-topbar-burger:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
  .mob-topbar-burger:active {
    transform: translateY(0.5px);
    box-shadow:
      0 0 2px rgba(31, 22, 17, 0.05),
      inset 0 1px 0 rgba(255, 255, 255, 0.7);
  }

  /* Builder-chat-mode topbar swap. Default brand lockup (diamond + wordmark
     + right spacer) hides; the draft pill (centre) + preview pill (right)
     show. Both use the same flat-disc shadow language as the burger. */
  .builder-project-pill,
  .builder-preview-pill { display: none; }
  body.is-builder-chat [data-mob-default-brand] { display: none !important; }
  /* 2026-05-22 V6 burger styling is now in the base .mob-topbar-burger
     rule at L1481, applied globally across all pages with a sidebar
     button. No builder-specific override needed here. */
  body.is-builder-chat .builder-project-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 44px;
    max-width: min(60vw, 280px);
    padding: 0 18px;
    border: 1px solid rgba(31, 22, 17, 0.13);
    border-bottom-color: rgba(31, 22, 17, 0.19);
    border-radius: 999px;
    background: linear-gradient(180deg, #FFFFFF 0%, #F8F8F8 100%);
    color: var(--ink);
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: -0.01em;
    cursor: pointer;
    box-shadow:
      0 1px 3px rgba(31, 22, 17, 0.07),
      0 2px 1px -1px rgba(31, 22, 17, 0.04),
      inset 0 1px 0 rgba(255, 255, 255, 0.9);
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    transition: all 0.15s ease;
  }
  .builder-project-pill__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }
  .builder-project-pill__chev {
    flex: 0 0 auto;
    color: var(--ink-mute);
  }
  body.is-builder-chat .builder-project-pill:active {
    transform: translateY(0);
    box-shadow:
      0 0 0 1px rgba(0, 0, 0, 0.06),
      0 1px 2px rgba(0, 0, 0, 0.04);
  }
  /* ── Project pill bottom sheet ──────────────────────────────────── */
  .builder-project-sheet {
    position: fixed;
    inset: 0;
    z-index: 200;
  }
  .builder-project-sheet__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.25);
    opacity: 0;
    transition: opacity 280ms ease;
  }
  .builder-project-sheet.is-open .builder-project-sheet__backdrop {
    opacity: 1;
  }
  .builder-project-sheet__panel {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    max-width: 460px;
    margin: 0 auto;
    background: #FFFFFF;
    border-top-left-radius: 20px;
    border-top-right-radius: 20px;
    box-shadow:
      0 -1px 0 rgba(0, 0, 0, 0.04),
      0 -16px 48px -12px rgba(20, 14, 8, 0.22);
    padding-bottom: max(20px, env(safe-area-inset-bottom, 20px));
    transform: translateY(100%);
    transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .builder-project-sheet.is-open .builder-project-sheet__panel {
    transform: translateY(0);
  }
  .builder-project-sheet__handle {
    width: 36px;
    height: 4px;
    border-radius: 999px;
    background: rgba(31, 22, 17, 0.14);
    margin: 10px auto 0;
  }
  .builder-project-sheet__credits {
    padding: 20px 22px 16px;
  }
  .builder-project-sheet__credits-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 10px;
  }
  .builder-project-sheet__credits-label {
    font-size: 15px;
    font-weight: 600;
    color: var(--ink, #1F1611);
  }
  .builder-project-sheet__credits-value {
    font-size: 15px;
    font-weight: 500;
    color: var(--ink, #1F1611);
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .builder-project-sheet__credits-value svg {
    opacity: 0.4;
  }
  .builder-project-sheet__bar {
    height: 6px;
    border-radius: 999px;
    background: rgba(31, 22, 17, 0.06);
    overflow: hidden;
    margin-bottom: 8px;
  }
  .builder-project-sheet__bar-fill {
    height: 100%;
    border-radius: 999px;
    transition: width 600ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .builder-project-sheet__bar-fill.is-healthy {
    background: linear-gradient(90deg, #34A853 0%, #4CAF50 100%);
  }
  .builder-project-sheet__bar-fill.is-warning {
    background: linear-gradient(90deg, #F9A825 0%, #FBC02D 100%);
  }
  .builder-project-sheet__bar-fill.is-critical {
    background: linear-gradient(90deg, #E53935 0%, #EF5350 100%);
  }
  .builder-project-sheet__credits-help {
    font-size: 12px;
    color: var(--ink-mute, #8A7B6D);
    display: flex;
    align-items: center;
    gap: 5px;
  }
  .builder-project-sheet__credits-help::before {
    content: '';
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--ink-mute, #8A7B6D);
    flex-shrink: 0;
  }
  .builder-project-sheet__divider {
    height: 1px;
    background: rgba(31, 22, 17, 0.06);
    margin: 0 22px;
  }
  .builder-project-sheet__nav {
    padding: 8px 10px;
  }
  .builder-project-sheet__item {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 12px;
    border-radius: 10px;
    font-size: 15px;
    font-weight: 450;
    color: var(--ink, #1F1611);
    text-decoration: none;
    border: 0;
    background: transparent;
    width: 100%;
    cursor: pointer;
    transition: background 0.12s;
    -webkit-tap-highlight-color: transparent;
    font-family: inherit;
  }
  .builder-project-sheet__item:active {
    background: rgba(31, 22, 17, 0.04);
  }
  .builder-project-sheet__item svg {
    color: var(--ink-mute, #6B5A4C);
    flex-shrink: 0;
  }

  body.is-builder-chat .builder-preview-pill {
    display: inline-flex;
    flex: 0 0 auto;
    width: 48px; height: 48px;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(31, 22, 17, 0.13);
    border-bottom-color: rgba(31, 22, 17, 0.19);
    border-radius: 50%;
    padding: 0;
    color: var(--ink);
    cursor: pointer;
    background: linear-gradient(180deg, #FFFFFF 0%, #F8F8F8 100%);
    box-shadow:
      0 1px 3px rgba(31, 22, 17, 0.07),
      0 2px 1px -1px rgba(31, 22, 17, 0.04),
      inset 0 1px 0 rgba(255, 255, 255, 0.9);
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    position: relative;
    z-index: 71;
    transition: all 0.15s ease;
  }
  body.is-builder-chat .builder-preview-pill:active {
    transform: translateY(0.5px);
    box-shadow:
      0 0 2px rgba(31, 22, 17, 0.05),
      inset 0 1px 0 rgba(255, 255, 255, 0.7);
  }
  body.is-builder-chat .builder-preview-pill:focus-visible {
    outline: 2px solid var(--orange);
    outline-offset: 2px;
  }
  /* Centre-align the title block now that the spacer is gone: rely on
     the flex:1 mob-topbar-title taking the slack between burger (44) +
     preview pill (44 + 10gap). */
  body.is-builder-chat .mob-topbar-title { justify-content: center; }

  .mob-topbar-title {
    flex: 1 1 auto;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    color: var(--ink);
  }
  /* Diamond brand mark inside the topbar, paired with the BookingSprint
     wordmark to read as a single brand lockup — matches Lovable's
     "heart + Lovable" header treatment. Sized for a 52px-tall bar
     with comfortable optical padding. */
  .mob-topbar-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    line-height: 0;
    flex-shrink: 0;
  }
  .mob-topbar-mark svg { width: 100%; height: 100%; display: block; }
  .mob-topbar-mark .bs-logo-mark__d { fill: var(--ink); }
  .mob-topbar-name {
    font-family: 'Geist', 'Inter Tight', 'Inter', sans-serif;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--ink);
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Right-side spacer keeps the brand lockup optically centred.
     Width matches the burger button's 48px footprint on the left. */
  .mob-topbar-spacer {
    flex: 0 0 auto;
    width: 48px; height: 48px;
    display: inline-block;
  }

  /* === Card-slide drawer (Lovable-style) =========================
     The page-card slides right to REVEAL the sidebar that's always
     parked at left:0. Sidebar no longer slides — it sits there
     permanently at a lower z-index, hidden by the card until the
     card moves out of the way. Rounded LEFT corners + soft shadow
     on the card give it the "lifted floating card" feel.

     The transform is driven by --drawer-offset (set on body) so the
     topbar, the wave-bands canvas, and the main content all glide
     together as one card. */
  body { --drawer-offset: 0px; }
  body.mob-drawer-open { --drawer-offset: min(360px, 84vw); }

  .side {
    position: fixed !important;
    top: 0; left: 0;
    height: 100vh;
    /* Slightly narrower drawer per user request — leaves a touch more
       peek of the card on the right when the drawer is open. */
    width: min(360px, 84vw);
    /* Stays put — no transform. The card slides over it (closed) or
       off it (open) to reveal / hide. */
    transform: none;
    /* Below the card so the card visually covers it when closed. */
    z-index: 1;
    border-right: 1px solid var(--hairline-2);
    display: flex !important;
    background: var(--topband);
  }

  /* Card surface — every element that should slide as part of the page
     "card" gets the same translate via --drawer-offset. The mob-topbar
     is fixed at top, the wave-bands canvas is fixed inset:0, and the
     shell's main-content children are in flow. All three transform
     in lockstep so the card moves as one unit. */
  .mob-topbar,
  .dash-hero-bg,
  .shell > main.dash,
  .shell > .pg,
  .shell > .rich-page {
    transform: translateX(var(--drawer-offset));
    transition: transform 0.32s cubic-bezier(0.2, 0, 0, 1),
                border-radius 0.32s cubic-bezier(0.2, 0, 0, 1),
                box-shadow 0.32s cubic-bezier(0.2, 0, 0, 1);
  }
  /* Sit the card content above the sidebar when closed (covers it) and
     stay above when open (the sidebar shows through where the card has
     slid away). */
  .mob-topbar { z-index: 70; }
  .shell > main.dash,
  .shell > .pg,
  .shell > .rich-page {
    position: relative;
    z-index: 10;
    /* Opaque card surface — on empty-hero pages the wave-bands sit at
       z-index 2 (covering the sidebar in the closed state) and main.dash
       is transparent so the waves show through. On non-empty / sub-route
       pages no waves are rendered, so main.dash + .pg / .rich-page paint
       linen themselves to cover the sidebar. */
    background: var(--topband);
    /* Extend to fill the viewport even when the page content is
       shorter than the screen. Without this, sub-routes with little
       content (e.g. Drafts on a fresh account) end mid-screen and
       the sidebar's foot (Settings / Need a hand / business name)
       shows through the gap below. 100dvh — 52px topbar so we don't
       overshoot past the visible viewport on iOS Safari. */
    min-height: calc(100dvh - 52px);
  }
  /* Empty-hero exception: main.dash must stay transparent so the waves
     (sitting at z-index 2 underneath) remain visible through the page. */
  body.is-empty-dash .shell > main.dash { background: transparent; }

  /* Rounded LEFT corners + clean outline edge when the card is slid out.
     Two-layer box-shadow on the card elements:
       1. -1px 0 0 0 — a hairline that hugs the rounded curve, giving
          a crisp outlined edge so the corner reads as a defined card.
       2. -3px 0 14px -10px — a very soft drop for "lift", much
          lighter than before so it no longer paints a grey smudge
          into the sidebar area at the corner.
     Topbar also gets padding-left:22px so the burger clears the
     rounded mask. Radius 22px across all card surfaces. */
  body.mob-drawer-open .mob-topbar {
    border-top-left-radius: 22px;
    overflow: hidden;
    padding-left: 22px;
    box-shadow:
      -1px 0 0 0 rgba(31, 22, 17, 0.08),
      -6px 0 22px -8px rgba(31, 22, 17, 0.18);
  }
  body.mob-drawer-open .dash-hero-bg {
    border-top-left-radius: 22px;
    border-bottom-left-radius: 22px;
    overflow: hidden;
  }
  body.mob-drawer-open .shell > main.dash,
  body.mob-drawer-open .shell > .pg,
  body.mob-drawer-open .shell > .rich-page {
    border-bottom-left-radius: 22px;
    box-shadow:
      -1px 0 0 0 rgba(31, 22, 17, 0.08),
      -6px 0 22px -8px rgba(31, 22, 17, 0.18);
  }

  /* The sidebar's hairline border-right was adding a second grey edge
     right next to the card's outline — visible as a thin line stacking
     against the card's hairline at the seam. With the card now carrying
     its own clean edge, the sidebar's border-right is redundant on
     mobile and reads as visual noise. !important needed because the
     [data-theme="light"] .side rule outside this media query wins
     specificity over a plain .side. */
  .side { border-right: 0 !important; }

  /* Backdrop: transparent. Tap-to-close click handler still wired; we
     just don't darken anything because the page card itself is now
     the visible surface to tap.

     CRITICAL: the backdrop must NOT cover the sidebar area. It used
     to be inset:0, which at z-index 80 sat above the sidebar (z:1)
     and the topbar (z:70) — so a tap on a sidebar nav item went to
     the backdrop's click handler (closeDrawer) instead of the link's
     native navigation. Same for touchstart. Net effect: tapping
     sidebar items closed the drawer without navigating, and you
     couldn't scroll the sidebar with touch either. Position the
     backdrop to start where the sidebar ends so the sidebar stays
     fully tappable. */
  .mob-drawer-backdrop {
    display: block;
    position: fixed;
    top: 0;
    bottom: 0;
    left: var(--drawer-offset);
    right: 0;
    background: transparent;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.22s cubic-bezier(0.2, 0, 0, 1),
                left 0.32s cubic-bezier(0.2, 0, 0, 1);
    z-index: 80;
  }
  body.mob-drawer-open .mob-drawer-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  .mob-drawer-close {
    display: inline-flex;
    align-items: center; justify-content: center;
    position: absolute;
    top: 12px; right: 12px;
    width: 28px; height: 28px;
    background: transparent;
    border: 0;
    border-radius: 6px;
    color: var(--ink-mute);
    cursor: pointer;
    z-index: 1;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0.1);
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
  }
  .mob-drawer-close:hover { background: var(--cream-3); color: var(--ink); }
  .mob-drawer-close:active { background: var(--cream-3); color: var(--ink); }
  .mob-drawer-backdrop { touch-action: manipulation; }

  /* iOS-safe body-scroll lock when the drawer is open. overflow:hidden on
     body is silently ignored by iOS Safari; position:fixed actually works.
     We don't bother saving/restoring scroll position since the drawer
     close is fast enough that the user doesn't notice. */
  body.mob-drawer-open {
    overflow: hidden;
    position: fixed;
    width: 100%;
    top: 0; left: 0; right: 0;
  }

  /* NUCLEAR overflow shield. The page kept rendering wider than the
     viewport on iOS Safari ("page slides sideways"). We don't trust any
     individual rule — apply width + max-width + overflow-x at every
     possible level so nothing can escape the viewport.
     - html: hard width-cap
     - body: hard width-cap, position relative for fixed children
     - * direct children of body: max-width: 100vw to catch SPA roots
     - touch-action pan-y prevents horizontal touch-pan from showing
       beyond the viewport edge during scroll-snap glitches */
  html {
    width: 100%;
    max-width: 100vw;
    overflow-x: hidden;
  }
  body {
    width: 100%;
    max-width: 100vw;
    overflow-x: hidden;
    position: relative;
    touch-action: pan-y pinch-zoom;
  }
  /* Every direct child of body must respect viewport width. The 100vw cap
     covers fixed-positioned children (mob-topbar, drawer, backdrop) which
     bypass body's width contraint. */
  body > * {
    max-width: 100vw;
    box-sizing: border-box;
  }
  /* Stop long unbroken strings (URLs, code, phone numbers) from forcing
     viewport-wide overflow. */
  .set-content, .set-card, .set-block, .set-row,
  .dash, .pg, .rich-page, [data-rich-mount] {
    word-break: break-word;
    overflow-wrap: anywhere;
  }
  /* The .usage-row min-width:220px was a desktop assumption that overflows
     when stacked single-column on mobile. */
  .usage-row { min-width: 0 !important; }

  /* Tab strip MUST scroll horizontally even though body overflow-x is
     hidden. Force its own scroll container and ensure no fade mask is
     ever applied. */
  .set-tabs {
    overflow-x: auto !important;
    overflow-y: hidden !important;
    -webkit-overflow-scrolling: touch !important;
    width: 100%;
    max-width: 100%;
  }
  .set-tab { flex: 0 0 auto !important; }

  /* Chevron buttons cause horizontal overflow on mobile (positioned at
     left:-4px / right:-4px outside their wrapper). Touch users swipe the
     strip directly; they don't need the chevrons. Hide them. */
  .set-tabs-chev { display: none !important; }
  .set-tabs-wrap { padding: 0 !important; }

  /* Brute-force defensive overflow shield. Anything inside the page that
     tries to be wider than the viewport gets clipped. The single biggest
     cause of the "page slides sideways" symptom on mobile. */
  .shell, .dash, .pg, .rich-page, .set-page, .set-content, .set-card, .set-block,
  main, [data-rich-mount], [data-main-root] {
    max-width: 100vw !important;
    box-sizing: border-box;
    overflow-x: hidden;
  }
  /* Allow only the tab strips themselves to scroll horizontally. */
  .set-tabs { overflow-x: auto !important; }

  /* Group divider inside the single flat strip (small vertical hairline
     between Account / Bookings / Site & SEO etc.). */
  .set-tab-sep {
    display: inline-block;
    width: 1px;
    height: 18px;
    background: var(--hairline-2);
    margin: 0 8px;
    flex: 0 0 1px;
    align-self: center;
  }

  /* Sticky save bar at bottom of every settings tab — stack on mobile so
     the buttons don't overflow off-screen. Horizontal margin so the bar
     sits inside the page padding rather than spilling under the edge. */
  .set-actions {
    flex-direction: column !important;
    align-items: stretch !important;
    justify-content: flex-start !important;
    gap: 10px !important;
    margin: 4px 12px 0 !important;
    padding: 12px 14px !important;
  }
  .set-actions > div:last-child {
    display: flex !important;
    gap: 8px !important;
    width: 100%;
  }
  .set-actions > div:last-child > .btn {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    text-align: center;
  }

  /* Defensive width caps so no card-internal element pushes the page wide. */
  .set-content, .set-card, .set-block { max-width: 100%; min-width: 0; }
}

/* === Mobile bottom-tab nav ============================================== */
.mobile-nav {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  background: var(--glass-bg);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-top: 1px solid var(--hairline-2);
  padding: 6px 4px calc(env(safe-area-inset-bottom, 0px) + 6px);
  z-index: 50;
  grid-template-columns: repeat(5, 1fr);
  gap: 2px;
  align-items: end;
}
/* Bottom-tab nav superseded by the hamburger drawer at <720px (see the
   inline shell rules in dashboard-v2-page.html). Keep the styles around
   in case we want to bring it back, but never show it. */
.mobile-nav { display: none !important; }

.mob-tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  padding: 6px 4px 4px;
  text-decoration: none;
  color: var(--ink-mute);
  font-size: 0.62rem; font-weight: 500;
  letter-spacing: 0.01em;
  border-radius: 8px;
  position: relative;
  min-height: 52px;
  transition: color 0.12s, background 0.12s;
}
.mob-tab svg { color: currentColor; transition: color 0.12s; }
.mob-tab:hover, .mob-tab:focus-visible { color: var(--ink); background: var(--cream-2); }
.mob-tab.is-active { color: var(--ink); }
.mob-tab.is-active svg { color: var(--ink); }

.mob-tab-badge {
  position: absolute;
  top: 2px; right: 18%;
  background: var(--ink);
  color: var(--cream);
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.56rem; font-weight: 600;
  letter-spacing: 0.04em;
  padding: 1px 5px;
  border-radius: 999px;
  min-width: 16px; text-align: center;
  line-height: 1.3;
}

/* Center "+ New" CTA tab */
.mob-tab.is-cta { color: var(--ink); }
.mob-tab-cta {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px;
  background: var(--ink);
  color: #fff;
  border-radius: 12px;
  margin-bottom: 1px;
  box-shadow: 0 2px 6px rgba(31, 22, 17, 0.18);
  transition: background 0.12s, transform 0.12s;
}
.mob-tab.is-cta:hover .mob-tab-cta { background: var(--ink-2); transform: translateY(-1px); }

/* === Anchor-as-button text-decoration cleanup ============================= */
a.upsell-cta, a.upsell-path, a.quick-card { text-decoration: none; }
a.upsell-cta { color: #fff; }
a.upsell-cta:visited { color: #fff; }
a.upsell-path { color: inherit; }
a.upsell-path:visited { color: inherit; }
a.quick-card { color: var(--ink); }
a.quick-card:visited { color: var(--ink); }

