/* public/css/builder-mobile.css — Phase 2 of the refactor (REFACTOR_PLAN.md §7).
   Copied verbatim from public/dashboard-v2.css ranges:
     11182..11357  (tablet/mobile builder input overrides)
     12552..13015  (cards-view mobile carousel + topbar fade)
   Plus 2026-05-29 founder-ask additions: cards-view URL bar + swipe-dot
   indicator + the preview/sections card visual fixes.
*/

  .builder-shell--centered .builder-input-box {
    /* AI builder composer — match /sandbox .lov-comp EXACTLY.
       Cream surface (#F5F5F5), 28px radius, 18/18/14 padding.
       background-image: none to clear any inherited gradients from
       earlier CSS layers.
       Founder 2026-05-30: soft drop shadow added so the composer
       lifts off the page bg slightly (was box-shadow:none). */
    background: #F5F5F5 !important;
    background-image: none !important;
    padding: 18px 18px 14px !important;
    border-radius: 28px !important;
    border: 1px solid rgba(31, 22, 17, 0.04) !important;
    box-shadow:
      0 1px 2px rgba(31, 22, 17, 0.04),
      0 6px 18px -8px rgba(31, 22, 17, 0.12),
      0 12px 32px -16px rgba(31, 22, 17, 0.10) !important;
    gap: 8px;
  }
  /* Coming-soon buttons (..., mic) carry [disabled] in markup so the
     base :disabled rule fades them to 0.55, which the founder read as
     "different shade" vs +/send. Restore full opacity inside the
     builder — these are permanent placeholders, the dimmed look is
     misleading. Keep cursor:not-allowed so the hover affordance
     still signals "not interactive yet". */
  .builder-shell--centered .builder-input-more:disabled,
  .builder-shell--centered .builder-input-mic:disabled,
  .builder-shell--centered .builder-input-map:disabled {
    opacity: 1 !important;
    cursor: not-allowed;
  }
  /* Send button — was dark ink fill on prod, sandbox uses a white
     circle matching the other ghost buttons. Override to white +
     dark-arrow icon so the composer reads as one matched set. */
  .builder-shell--centered .builder-input-send {
    background: linear-gradient(180deg, #FFFFFF 0%, #F5F5F5 100%) !important;
    color: var(--ink) !important;
    border: 1px solid rgba(31, 22, 17, 0.10) !important;
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.9),
      inset 0 -1px 0 rgba(31, 22, 17, 0.04),
      0 1px 2px rgba(31, 22, 17, 0.06),
      0 2px 6px -2px rgba(31, 22, 17, 0.08) !important;
  }
  .builder-shell--centered .builder-input-send:hover:not(:disabled) {
    background: linear-gradient(180deg, #FFFFFF 0%, #EAEAEA 100%) !important;
  }
  .builder-shell--centered .builder-input-textarea {
    font-size: 16px;
    min-height: 32px;
    max-height: 120px;
    line-height: 1.5;
    background: transparent;
    padding: 4px 4px 0;
    field-sizing: content;
  }
  /* When empty (showing the placeholder), constrain to a single line
     and clip any placeholder overflow so a long example doesn't bloat
     the composer height. Without this, "Sparky in Newtown. EV chargers
     and switchboards." wraps to 2-3 lines and the composer ends up
     50-80px taller than it needs to be. */
  .builder-shell--centered .builder-input-textarea:placeholder-shown {
    max-height: 32px;
    overflow: hidden;
  }
  .builder-shell--centered .builder-input-textarea::placeholder {
    color: var(--ink-soft);
  }
  .builder-shell--centered .builder-input-bar {
    margin-top: 0;
    min-height: 36px;
  }
  .builder-shell--centered .builder-input-send {
    width: 38px !important;
    height: 38px !important;
    border-radius: 50% !important;
  }
  .builder-shell--centered .builder-input-send svg {
    width: 16px;
    height: 16px;
  }
  /* Founder 2026-05-30 v2: match sandbox /lov-comp exactly — 38px
     circle buttons (not 28px) with 3D gradient inheriting from
     composer-shared.css base. Mode pill matches scale. */
  .builder-shell--centered .builder-input-action,
  .builder-shell--centered .builder-input-more,
  .builder-shell--centered .builder-input-mic,
  .builder-shell--centered .builder-input-map {
    width: 38px !important;
    height: 38px !important;
    border-radius: 50% !important;
  }
  .builder-shell--centered .builder-input-mode-pill {
    height: 32px !important;
  }
  .builder-shell--centered .builder-input-action svg,
  .builder-shell--centered .builder-input-more svg,
  .builder-shell--centered .builder-input-mic svg,
  .builder-shell--centered .builder-input-map svg {
    opacity: 1;
    width: 16px;
    height: 16px;
  }
  .builder-shell--centered .builder-input-model {
    display: none;
  }

  /* Layout: composer MUST pin to the bottom of the chat pane in every
     state — empty, mid-chat, ready. Without this, once a chat row lands
     and .builder-instruction (which had margin-bottom:auto) gets hidden,
     the composer floats up into the middle of the page because nothing
     else is consuming the slack. margin-top:auto on .builder-input-wrap
     pushes the composer down regardless of which sibling above it is
     visible. */
  .builder-shell--centered .builder-input-wrap {
    margin-top: auto !important;
  }
  /* When the chat thread has content, kill the cascade that pushed the
     instruction (margin-bottom:auto) so chat-body keeps a normal
     stacking flow and the input stays glued to the bottom edge. */
  .builder-shell--centered:has(.builder-chat-body:not(:empty)) .builder-instruction {
    margin-bottom: 0 !important;
  }
  /* Phase 4.6: chat-body grows to fill the pane above the fixed
     composer. Content flows top-down (default flex-start); for the
     form, the JS scrolls the form's top into view on append so the
     user lands at "Business name". For short chat content (thinking
     state), content sits at the top with empty space below the
     content. That gap reads as "AI is working" rather than a layout
     bug, especially since the composer below has a soft fade scrim
     making it feel like content gently disappears into it. */
  .builder-shell--centered .builder-chat-body {
    flex: 1 1 0% !important;
    overflow-y: auto;
    min-height: 0;
  }
  /* Composer pinned to the VIEWPORT bottom (Claude-exact), not the
     pane bottom. With this the chat bar is always at the bottom of
     the screen whether the chat is empty, mid-thinking, mid-form, or
     a long thread — no whitespace floating above it. Width hugs the
     pane edges (left/right 16) so it doesn't stretch edge-to-edge
     over the safe-area side insets. z-index 5 keeps it above the
     scrolling chat content. The padding-top + linear-gradient
     bottom fade creates a soft scrim so chat content scrolling
     under the composer fades out cleanly instead of cutting
     hard against the white surface. */
  /* Phase 4.8 fix (code-review): gate position:fixed on the empty
     state ONLY. In the ready state, the chat-pane itself is a
     position:fixed bottom sheet (line ~6850) that contains chat-body
     + composer. If the composer is ALSO position:fixed, it escapes
     the sheet and floats at viewport bottom independently — produces
     a duplicate-composer effect (one inside the sheet, one at the
     viewport bottom). Ready state keeps the composer in normal flow
     inside the sheet so the sheet drag still encloses it. */
  .builder-shell--centered:not([data-builder-state="ready"]) .builder-input-wrap {
    position: fixed;
    left: 16px;
    right: 16px;
    bottom: 0;
    z-index: 5;
    /* Zero padding-top: input-box's outer top edge sits exactly at
       composer.outer.top. Combined with shellSyncChatPanePadBot's
       reserve of EXACTLY composer.height (no extra), the chat-body
       content's bottom lands at composer.outer.top = input-box.top.
       The Build button's bottom edge touches the input-box top edge
       with 0 visible cream gap — true "right against the chat bar".
       The 8px fade band still exists below composer.outer.top but
       is fully covered by the input-box's opaque paper bg, so it's
       only visible during iOS rubber-band overscroll when content
       briefly slides past the input-box and into the fade zone. */
    padding-top: 0;
    padding-bottom: max(12px, env(safe-area-inset-bottom));
    /* Wrap bg matches the sidebar / topbar (var(--topband), #FDFDFD) so
       the bottom-of-page chrome reads as one continuous warm panel —
       sidebar, topbar, composer, safe-area-inset band all the same
       colour. Founder direction 2026-05-22. */
    background: linear-gradient(to bottom,
      rgba(253, 253, 253, 0) 0,
      var(--topband, #FDFDFD) 8px,
      var(--topband, #FDFDFD) 100%);
    margin-top: 0 !important;
  }
  /* 2026-05-22 founder ask — Lovable-parity: the chat thread should
     scroll BEHIND the fixed composer (only cut off at the topbar),
     not stop above it. Previously we set padding-bottom on the
     .builder-chat-pane which clipped the chat-body's outer height so
     content couldn't extend past the composer's top edge — that read
     as the form being "cut off early".
     Fix: pane padding-bottom = 0 so chat-body fills the full pane
     height (down to the viewport bottom). The composer-height reserve
     moves INSIDE the chat-body's scroll content (padding-bottom
     below) so the last item still rests above the composer when
     scrolled to the end, while older content can scroll up through
     the composer's fade band. shellSyncChatPanePadBot (JS) sets the
     exact composer height as inline style on .builder-chat-body.
     Scoped to :has(...) so the empty state (no chat yet) keeps its
     small safe-area-inset bottom padding for the hero+chip stack. */
  .builder-shell--centered:not([data-builder-state="ready"]) .builder-chat-pane:has(.builder-chat-body:not(:empty)) {
    padding-bottom: 0 !important;
  }
  /* Default reserve before the JS measures the composer (170px is the
     same upper-bound the JS targeted: ~120 input-box + ~14 fade band +
     up to 34 safe-area). Inline style from shellSyncChatPanePadBot
     overrides this with the precise height once the composer mounts. */
  .builder-shell--centered:not([data-builder-state="ready"]) .builder-chat-body {
    padding-bottom: 170px !important;
  }

  /* Founder 2026-05-30: bring message text closer to the screen edges
     on mobile. Before this fix the gap chain was:
       chat-pane padding 16px + chat-body padding 18px + msg-row padding 12px
       = 46px from each screen edge — text felt too inset.
     The chat-pane outer gutter (16px) is enough breathing room on its own;
     chat-body's 18px horizontal padding + msg-row's 12px were doubling up.
     Zero both so bubbles sit at 16px from each edge — closer to iMessage /
     modern chat-app rhythm. */
  body.is-builder-chat:not(.is-builder-cards-view) .builder-shell--centered .builder-chat-body {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
  body.is-builder-chat:not(.is-builder-cards-view) .builder-shell--centered:not([data-builder-state="ready"]) .builder-msg-row,
  body.is-builder-chat:not(.is-builder-cards-view) .builder-shell--centered:not([data-builder-state="ready"]) .builder-form-row {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }

  /* PAGE-SCROLL MODE for the mobile chat thread has been moved to
     /public/dashboard-v2-page.css (canonical block at the bottom of
     that file, 2026-05-22 rewrite v5). Keeping the rules here was
     causing cascade fights: every iteration we'd patch this block,
     it would lose to the inline <style> at dashboard-v2-page.html,
     and the layout would land wrong. The new home is a single
     authoritative block in the page-specific stylesheet (loads
     after dashboard-v2.css, beats earlier rules without a
     specificity arms race). */
  /* Suggestion chips MUST hide as soon as the user starts chatting,
     even though they have display:flex !important on the empty-state
     mobile rule below. Same selector specificity + later in the
     cascade = wins. */
  .builder-shell--centered:has(.builder-chat-body:not(:empty)) .builder-suggestion-row {
    display: none !important;
  }

/* ──── builder-mobile.css range 2: source lines 12552..13015 ──── */
/* ============================================================================
   Cards view (mobile only) — 2026-05-22
   Tap the top-right preview pill (.builder-preview-pill) to zoom the builder
   out and reveal three side-by-side cards: Chat / Preview / Sections. Swipe
   horizontally (scroll-snap) to peek; tap a card body to zoom in and exit
   cards-view with that pane active.

   Single mode (default): existing rules unchanged. Only the active pane
   shows; others stay mounted but hidden via existing CSS.

   Cards mode: body.is-builder-cards-view applied. The .builder-shell flips
   from its column layout to a horizontal scroll-snap row, every .builder-pane
   becomes a flex item card, .builder-pane__card-head shows at the top of
   each card, the bottom composer hides, the topbar swaps project pill for
   a Back arrow. Live DOM stays mounted (chat keeps streaming, iframe keeps
   loading) — no screenshots.
   ========================================================================== */

/* Card head + close button — hidden by default (single mode). */
.builder-pane__card-head { display: none; }
.builder-cards-back { display: none; }
/* Card wrapper + label — single mode: wrap is "invisible" (display:contents
   passes children through to the shell grid), label is display:none.
   Cards-view re-enables both via the @media block below. */
.builder-card-wrap { display: contents; }
.builder-card-label { display: none; }
.builder-cards-dock { display: none; }
/* URL bar + swipe dots are cards-view-only; hidden by default everywhere. */
.builder-cards-urlbar { display: none; }
.builder-cards-dots { display: none; }
/* URL bar + swipe dots are cards-view-only; hidden by default everywhere. */
.builder-cards-urlbar { display: none; }
.builder-cards-dots { display: none; }

@media (max-width: 720px) {
  /* Activate cards view --------------------------------------------------- */
  body.is-builder-cards-view {
    background-color: var(--cream-3) !important;
    overflow: hidden;
  }
  /* 2026-05-22 v8d — sidebar must REMAIN renderable in cards-view so
     the burger menu can open it (founder ask: 'make sure the sidebar
     button works cleanly in the cards section as well'). Previous
     rule set `display: none !important` which made the burger a
     no-op. Use visibility-hidden-when-closed + visible-when-open
     instead (same pattern as builder-chat single mode). The original
     concern about the drawer-footer 'leaking under the cards at the
     bottom of the viewport' was visible because the cards bg was
     transparent; with v8c the bg is solid --topband so any leak is
     no longer an issue. */
  body.is-builder-cards-view:not(.mob-drawer-open) .side {
    visibility: hidden !important;
  }
  body.is-builder-cards-view.mob-drawer-open .side {
    visibility: visible !important;
    display: flex !important;
  }
  body.is-builder-cards-view .mob-topbar {
    background-color: var(--cream-3) !important;
    color: var(--ink);
    position: fixed !important;
    top: 0; left: 0; right: 0;
    z-index: 80;
  }
  /* 2026-05-22 Cards-view burger keeps the V6 gradient + shadow from
     the base rule (dashboard-v2.css L1481). This rule used to revert
     to a flat var(--paper) bg + light shadow — overrode V6. Removed. */
  body.is-builder-cards-view .builder-project-pill { display: none !important; }
  /* 2026-05-29 — the right-side preview-pill stays in cards-view but its
     handler becomes a TOGGLE (open ↔ close). The URL bar replaces the
     centred Back pill in the topbar; tapping the preview-pill (or
     swiping the cards) is now the only way out. */
  body.is-builder-cards-view .builder-cards-back { display: none !important; }

  /* 2026-05-29 — Lovable/v0-style URL bar in the topbar centre. Fills
     the space between the burger (40px wide) and the preview-pill
     (~44px wide). Read-only address pill with a globe icon, the host,
     and a "Draft" badge or open-in-new-tab affordance. The host text
     ellipsises if the subdomain is long; the trailing " /" stays
     visible. */
  body.is-builder-cards-view .builder-cards-urlbar {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 38px;
    padding: 0 12px;
    border: 1px solid rgba(31, 22, 17, 0.10);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.7);
    color: var(--ink);
    font-family: inherit;
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    box-shadow:
      0 1px 2px rgba(31, 22, 17, 0.04),
      inset 0 1px 0 rgba(255, 255, 255, 0.7);
    /* Centre absolutely so it pins to the topbar middle regardless of
       left/right element widths. */
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    /* Cap the bar's width so long subdomains don't push edges off; the
       host text inside truncates with ellipsis when needed. */
    max-width: calc(100vw - 120px);
    min-width: 0;
  }
  body.is-builder-cards-view .builder-cards-urlbar__icon {
    flex: 0 0 auto;
    color: var(--ink-mute);
  }
  body.is-builder-cards-view .builder-cards-urlbar__url {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* Mono-ish feel without committing to a monospace font — letters
       feel "url-y" via the mute colour + tracking. */
    letter-spacing: 0.005em;
  }
  body.is-builder-cards-view .builder-cards-urlbar__host {
    color: var(--ink);
  }
  body.is-builder-cards-view .builder-cards-urlbar__path {
    color: var(--ink-mute);
  }
  /* Draft badge — small pill on the right. Hidden when site is live
     (the JS swaps the hidden attribute on the urlbar + draft span). */
  body.is-builder-cards-view .builder-cards-urlbar__draft {
    flex: 0 0 auto;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--ink-mute);
    padding: 2px 6px;
    border: 1px solid rgba(31, 22, 17, 0.13);
    border-radius: 4px;
    line-height: 1.1;
  }
  /* Draft-state visual cue: the host name is greyed when no subdomain
     is claimed. Toggled by data-state on the parent .builder-cards-urlbar. */
  body.is-builder-cards-view .builder-cards-urlbar[data-state="draft"] .builder-cards-urlbar__host {
    color: var(--ink-mute);
  }
  body.is-builder-cards-view .builder-cards-urlbar[data-state="published"] .builder-cards-urlbar__draft {
    display: none;
  }
  body.is-builder-cards-view .builder-cards-urlbar:active {
    transform: translate(-50%, calc(-50% + 1px));
    background: rgba(255, 255, 255, 0.85);
  }
  body.is-builder-cards-view .mob-topbar { position: fixed !important; }
  body.is-builder-cards-view [data-mob-default-brand] { display: none !important; }
  body.is-builder-cards-view .mob-topbar-title { justify-content: center; }
  body.is-builder-cards-view .mob-topbar-spacer { display: none !important; }

  /* Shell becomes a horizontal scroll-snap row -------------------------- */
  body.is-builder-cards-view .builder-shell {
    position: fixed !important;
    inset: 52px 0 0 0 !important;
    display: flex !important;
    flex-direction: row !important;
    /* center vertically so cards sit with breathing room above + below */
    align-items: center !important;
    /* Tighter gap + side padding so cards are close together, Replit-style. */
    gap: 8px !important;
    padding: 24px 6vw 110px !important;
    overflow-x: auto !important;
    overflow-y: hidden !important;
    scroll-snap-type: x mandatory !important;
    -webkit-overflow-scrolling: touch !important;
    background-color: var(--cream-3) !important;
    z-index: 60 !important;
    grid-template-columns: none !important;
    grid-template-rows: none !important;
    height: auto !important;
    min-height: 0 !important;
  }
  /* 2026-05-29 — Swipe-dot indicator above the dock. Three small pills,
     active one filled, tracks which card is centred in the scroll-snap
     row. Updated by the scroll listener wired in publish.js. */
  body.is-builder-cards-view .builder-cards-dots {
    display: flex;
    gap: 6px;
    align-items: center;
    justify-content: center;
    position: fixed;
    left: 0; right: 0;
    /* Tucked just above the dock (which has padding-bottom + safe-area). */
    bottom: calc(72px + env(safe-area-inset-bottom));
    z-index: 70;
    pointer-events: none;
  }
  body.is-builder-cards-view .builder-cards-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background: rgba(31, 22, 17, 0.18);
    transition: background 180ms cubic-bezier(0.22, 1, 0.36, 1),
                width 180ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  body.is-builder-cards-view .builder-cards-dot.is-on {
    background: var(--ink);
    width: 18px;
  }

  /* Bottom dock — Publish + Share. Floats below the cards, fixed at
     the viewport bottom inside the cards-view backdrop. */
  body.is-builder-cards-view .builder-cards-dock {
    display: flex;
    gap: 12px;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    padding: 14px 16px max(20px, env(safe-area-inset-bottom));
    background: linear-gradient(to bottom, rgba(229, 227, 221, 0) 0%, var(--cream-3) 28%);
    z-index: 70;
    justify-content: center;
    pointer-events: none;
  }
  body.is-builder-cards-view .builder-cards-dock__btn {
    pointer-events: auto;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 44px;
    padding: 0 20px;
    border-radius: 999px;
    border: 1px solid rgba(31, 22, 17, 0.13);
    border-bottom-color: rgba(31, 22, 17, 0.19);
    background: linear-gradient(180deg, #FFFFFF 0%, #F8F8F8 100%);
    color: var(--ink);
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    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;
  }
  body.is-builder-cards-view .builder-cards-dock__btn[data-cards-dock="publish"] {
    background: linear-gradient(180deg, #2A2520 0%, #1F1611 100%);
    color: #FAFAFA;
    border-color: #1F1611;
    border-bottom-color: #0F0B08;
    box-shadow:
      0 1px 3px rgba(31, 22, 17, 0.2),
      inset 0 1px 0 rgba(255, 255, 255, 0.08);
  }
  body.is-builder-cards-view .builder-cards-dock__btn:active {
    transform: translateY(1px);
  }
  body.is-builder-cards-view .builder-shell::-webkit-scrollbar { display: none; }

  /* Every pane becomes a card ------------------------------------------- */
  /* In cards-view, the .builder-card-wrap becomes the flex item (NOT the
     .builder-pane). The wrap is a vertical flex column containing the
     floating label + the pane. */
  body.is-builder-cards-view .builder-shell--centered .builder-card-wrap {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch;
    gap: 10px;
    flex: 0 0 88vw !important;
    width: 88vw !important;
    max-width: 88vw !important;
    scroll-snap-align: center !important;
    scroll-snap-stop: always !important;
    transform: none;
  }
  /* Label sits above the pane — small text + icon, centred. Replit-style. */
  body.is-builder-cards-view .builder-shell--centered .builder-card-label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    color: var(--ink-mute);
    font-family: inherit;
    font-size: 0.92rem;
    font-weight: 500;
    letter-spacing: -0.005em;
    flex: 0 0 auto;
    padding: 4px 8px;
    user-select: none;
    -webkit-user-select: none;
  }
  body.is-builder-cards-view .builder-shell--centered .builder-pane {
    /* Override the position:fixed bottom-sheet behaviour from
       data-builder-state="ready" on .builder-chat-pane. */
    position: relative !important;
    inset: auto !important;
    top: auto !important;
    bottom: auto !important;
    left: auto !important;
    right: auto !important;
    transform: none;
    pointer-events: auto !important;
    /* Pane fills its wrap (the wrap is the flex item now). */
    flex: 1 1 auto !important;
    width: 100% !important;
    max-width: 100% !important;
    /* Compact card — 42vh so it reads as a thumbnail. min-height: 0
       MUST be set explicitly because .builder-shell .builder-pane has
       min-height: calc(100dvh - 52px) at line 6947 (mobile pane fill),
       and min-height beats max-height in the CSS cascade. Without this
       override, the pane stretches to ~790px on mobile regardless of
       max-height. */
    min-height: 0 !important;
    height: 42vh !important;
    max-height: 380px !important;
    border-radius: 20px !important;
    overflow: hidden !important;
    background: var(--paper) !important;
    box-shadow:
      0 12px 36px rgba(0, 0, 0, 0.50),
      0 2px 8px rgba(0, 0, 0, 0.30) !important;
    scroll-snap-align: center !important;
    scroll-snap-stop: always !important;
    display: flex !important;
    flex-direction: column !important;
    cursor: pointer !important;
    /* Force the preview-pane back to visible — its default display:none
       in non-ready states needs to be overridden so the 3rd card always
       renders in cards view. */
    visibility: visible !important;
  }
  /* Preview pane is hidden in non-ready single mode but must show in cards. */
  body.is-builder-cards-view .builder-shell--centered .builder-preview-pane {
    display: flex !important;
  }
  /* 2026-05-29 — iOS Safari hands sharp corners to <iframe> children
     of a rounded+overflow-clipped container; the iframe paints past
     the clip region. Apply the same border-radius + overflow:hidden
     to .builder-preview-body and force a compositing layer via
     translateZ so Safari respects the clip. */
  body.is-builder-cards-view .builder-shell--centered .builder-preview-body {
    border-radius: 20px !important;
    overflow: hidden !important;
    transform: translateZ(0);
    -webkit-mask-image: -webkit-radial-gradient(white, black);
  }
  /* 2026-05-29 — Sections card was clipping the lede ("Tap a section to
     expand its preview...") against the rounded top corner because the
     body only had 18px top padding; the rounded corner geometry ate
     ~8px of the first line. Bump top padding only in cards-view. */
  body.is-builder-cards-view .builder-sections-pane__body {
    padding-top: 26px !important;
  }
  /* Sections pane shows only in cards-view; hidden in single mode. */
  .builder-shell .builder-sections-pane { display: none; }
  body.is-builder-cards-view .builder-shell .builder-sections-pane {
    display: flex !important;
  }

  /* Card head shown in cards-view --------------------------------------- */
  body.is-builder-cards-view .builder-pane__card-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--hairline);
    background: var(--paper);
    flex-shrink: 0;
    color: var(--ink);
    font-size: 0.9rem;
    font-weight: 600;
    letter-spacing: -0.005em;
  }
  body.is-builder-cards-view .builder-pane__card-title {
    flex: 1 1 auto;
  }
  body.is-builder-cards-view .builder-pane__card-close {
    flex: 0 0 auto;
    width: 28px; height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--ink-mute);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }
  body.is-builder-cards-view .builder-pane__card-close:hover {
    background: var(--cream-2);
    color: var(--ink);
  }

  /* 2026-05-23 founder: input-wrap + suggestion-row + refine-row stay
     visible in cards-view as part of the 1:1 chat preview. Pointer
     events are killed in dashboard-v2-page.html so the card-tap-to-
     close gesture still works. sheet-handle (single-mode resize) and
     chat-tabs (those are separate cards in cards-view) stay hidden. */
  body.is-builder-cards-view .builder-sheet-handle { display: none !important; }
  body.is-builder-cards-view .builder-chat-tabs { display: none !important; }
  /* Also override the natural ":has()" hide on .builder-suggestion-row
     (which hides the chips once the chat has any messages) — in cards-
     view we want chips visible inside the card regardless of chat state. */
  body.is-builder-cards-view .builder-chat-pane .builder-suggestion-row { display: flex !important; }

  /* The chat body in cards-view fills the card under the head.
     Content scaling is handled by the parent pane's transform: scale(0.85)
     — that affects all descendants uniformly without zoom quirks. */
  body.is-builder-cards-view .builder-chat-pane .builder-chat-body {
    flex: 1 1 auto !important;
    overflow-y: auto !important;
    padding: 12px !important;
  }

  /* Sections pane body */
  .builder-sections-pane {
    /* base — single mode hidden by default rule above */
  }
  .builder-sections-pane__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 18px 16px;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  .builder-sections-pane__lede {
    margin: 0;
    font-size: 0.92rem;
    line-height: 1.5;
    color: var(--ink-mute);
  }
  .builder-sections-pane__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .builder-sections-pane__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    border: 1px solid var(--hairline);
    border-radius: 12px;
    background: var(--cream);
    color: var(--ink);
    font-size: 0.95rem;
    font-weight: 500;
  }
  .builder-sections-pane__item svg {
    color: var(--ink-mute);
    flex-shrink: 0;
  }
  .builder-sections-pane__footnote {
    margin: 6px 0 0;
    font-size: 0.78rem;
    color: var(--ink-soft);
    text-align: center;
    font-style: italic;
  }
  /* 2026-05-29 — Cards-view variant-picker items hold the click-row +
     (optional) form-toggle pills + the dropdown preview iframe inside
     a single <li>. The base rule above sets display:flex for the
     single-mode list; for the cards-view variant picker we need
     column layout so the row sits ABOVE its form-toggle pills and the
     preview-drop, matching single-pane behaviour. Without this the
     form-toggle gets squished to ~10px wide on the right of the row
     and each character of "Quote form" wraps to its own line. */
  body.is-builder-cards-view .builder-sections-pane__item--variants {
    flex-direction: column !important;
    align-items: stretch !important;
    justify-content: flex-start !important;
    padding: 0 !important;
    gap: 0 !important;
  }
  /* Shrink the dropdown preview iframe so it doesn't push the rest of
     the section list off the card. Single-mode caps at 360px (per the
     comment at dashboard-v2-page.css ~line 3946); cards-view squeezes
     to 240px because the card itself is only 380px tall. */
  body.is-builder-cards-view .builder-sections-pane__item-preview-drop {
    max-height: 240px !important;
  }
  body.is-builder-cards-view .builder-sections-pane__item-preview-drop iframe {
    max-height: 240px !important;
  }

  /* Entry animation — zoom OUT feel. Backdrop fades in over 320ms;
     panes scale-in from 1.12 (starts slightly LARGER than card size,
     so the user perceives the previously-fullscreen content shrinking
     into a card) with a 110ms stagger between cards. Total ~750ms,
     deliberately slow so the zoom-out gesture is felt, not snapped. */
  @keyframes builderCardsFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  @keyframes builderCardScaleIn {
    /* Starts slightly LARGER than card (1.08) and shrinks to natural
       (1.0). Perception: the previously full-bleed content shrinks
       into the card's smaller layout box. */
    from { transform: scale(1.08); opacity: 0; }
    to   { transform: scale(1);    opacity: 1; }
  }
  body.is-builder-cards-view:not(.is-builder-cards-closing) .builder-shell {
    animation: builderCardsFadeIn 0.32s cubic-bezier(0.2, 0, 0, 1) both;
  }
  body.is-builder-cards-view:not(.is-builder-cards-closing) .builder-shell--centered .builder-card-wrap {
    animation: builderCardScaleIn 0.52s cubic-bezier(0.16, 0.84, 0.36, 1) both;
    transform-origin: center center;
  }
  body.is-builder-cards-view:not(.is-builder-cards-closing) .builder-card-wrap:nth-child(2) { animation-delay: 0.11s; }
  body.is-builder-cards-view:not(.is-builder-cards-closing) .builder-card-wrap:nth-child(3) { animation-delay: 0.22s; }

  /* Exit animation — zoom IN feel. body.is-builder-cards-closing is
     applied by JS just before removing is-builder-cards-view. The
     panes scale UP to 1.18 + fade out (perception: the chosen card
     grows toward the viewer); the backdrop fades. Total ~380ms; JS
     then strips both classes and single-mode takes over. */
  @keyframes builderCardScaleOut {
    /* From natural (1.0) scales UP to 1.12 + fades. Perception: tapped
       card grows toward viewer as cards-view dissolves. */
    from { transform: scale(1);    opacity: 1; }
    to   { transform: scale(1.12); opacity: 0; }
  }
  @keyframes builderCardsFadeOut {
    from { opacity: 1; }
    to   { opacity: 0; }
  }
  body.is-builder-cards-closing .builder-shell {
    animation: builderCardsFadeOut 0.32s cubic-bezier(0.4, 0, 0.6, 1) forwards;
  }
  body.is-builder-cards-closing .builder-shell--centered .builder-card-wrap {
    animation: builderCardScaleOut 0.38s cubic-bezier(0.4, 0, 0.6, 1) forwards;
    transform-origin: center center;
  }

  @media (prefers-reduced-motion: reduce) {
    body.is-builder-cards-view .builder-shell,
    body.is-builder-cards-view .builder-shell--centered .builder-card-wrap,
    body.is-builder-cards-closing .builder-shell,
    body.is-builder-cards-closing .builder-shell--centered .builder-card-wrap {
      animation: none !important;
    }
  }
}
/* Desktop (>720px) — cards view not wired for v1; preview pill stays
   doing whatever its single-mode click does (currently nothing). */


/* ============================================================================
   Lovable-style topbar fade (2026-05-22)
   ============================================================================
   Founder wants chat content to "fade behind the buttons" like Lovable.
   After several hours iterating on backdrop-filter (which renders as solid
   on the founder's iOS Safari despite the standard hacks), parallel-agent
   research determined:
     - Lovable uses backdrop-filter on a sticky topbar (shadcn default).
     - Their pattern is fragile on iOS Safari when there's a non-trivial
       parent stacking context (which we have: main.dash z:10 +
       transform: translateX from the drawer pattern).
     - A gradient overlay below an opaque topbar gives the visual result
       reliably on every iOS Safari version (verified with iPhone UA
       puppeteer test — see /tmp/alt3.png).

   Architecture:
     - .mob-topbar stays solid var(--topband) (unchanged from base).
     - body.is-builder-chat::after is a fixed 60px gradient strip
       sitting at viewport 52-112, fading from --topband (top, full
       opacity) to transparent (bottom).
     - Scoped to :not(.mob-drawer-open) so the overlay doesn't paint
       over the sidebar drawer when it's open.
     - z-index 65 (above content z:10, below topbar z:70).
     - Content scrolling up through viewport 52-112 visually dissolves
       into the topbar's colour.
     - Content below viewport 112 renders fully clear.

   For the first message NOT to be hidden behind the topbar at rest,
   add padding-top to .builder-chat-body so the first message starts
   at viewport ~112 (clear of the overlay's faded zone).
   ============================================================================ */
@media (max-width: 720px) {
  /* 2026-05-22 founder ask v4 — kill the fade overlay. The topbar is
     now fully transparent (set in dashboard-v2-page.html inline CSS),
     so there's no frosted band to soften into the page; chat content
     scrolls clear behind the floating buttons and clips at the top of
     chat-body's overflow region. */
  body.is-builder-chat::after,
  body.is-builder-chat:not(.mob-drawer-open)::after {
    display: none !important;
  }
  body.is-builder-chat .builder-shell--centered .builder-chat-body {
    padding-top: 0 !important;
  }
}
