/* ============================================================================
   chapo-v4-theme.css — full-app visual override
   ----------------------------------------------------------------------------
   Every rule is scoped under `body.chapo-v4` (set by AppShellV4 on mount).
   Inert when the v4 shell is not active.

   PURPOSE
   This sheet guarantees 100% screen coverage. The five bespoke v4 pages
   (Home/Basket/Compare/Orders/Account) own their layout; THIS sheet restyles
   everything the bespoke pages do NOT replace:

     Pages    : Login, ShopHealth, Catalog (GroceryList), AiReport, Settings
     Modals   : every Bootstrap `.modal` across Home, GroceryList, Orders
                (address 3-step, grocery-list, suggestions, scan-receipt,
                 all-stores, report-history, processing, apply-list,
                 product-detail, fullscreen-image, order-detail, share)
     Global   : ConfirmDialog, ToastContainer, About modal, user menu
     Controls : buttons, cards, inputs, selects, alerts, badges, list-groups,
                tables, dropdowns, nav-tabs, nav-pills, spinners, form-checks

   CONVENTIONS
     - Tokens from chapo-tokens.css (--chapo-*).
     - Logical properties (margin-inline-*, inset-inline-*) for RTL safety.
     - !important only where it must beat Bootstrap utility !importants.
   ============================================================================ */

/* ---------- 0. Base ------------------------------------------------------- */
body.chapo-v4 {
    background: var(--chapo-bg);
    color: var(--chapo-text);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* ---------- 0a. Exactly one vertical scroller ----------------------------- */
/* `.page` (MainLayout) is `height:100dvh; overflow-y:auto` and is the element the
   sticky context bar sticks to, so it must stay THE scroller. Everything above it
   is pinned to the viewport so nothing else can scroll:

   - Without `overflow:hidden` here, iOS WKWebView also scrolls the document itself.
     You then have the document and `.page` scrolling the same gesture — the nested
     pair that makes the content scrollbar feel stuck and lets the top strip drift
     independently of the content under it.
   - `height:100%` (not min-height) stops the document growing past the viewport,
     which is what let the outer scroll engage in the first place. */
/* dvh rather than `height:100%`: 100% would resolve against <html>, which has no
   height of its own, so it would compute to `auto` and clip nothing. Sizing the body
   to the dynamic viewport directly keeps <html> from overflowing without needing a
   rule on <html> at all (and without leaning on :has(), which this shell has been
   bitten by in WKWebView before). */
body.chapo-v4 {
    height: 100dvh;
    overflow: hidden;
    overscroll-behavior-y: none;
}

/* The shell must not be TALLER than the scroller that contains it. It was
   `min-height:100vh` inside a `height:100dvh` parent: 100vh is the LARGE viewport
   (as if browser chrome were hidden) and is therefore >= 100dvh — always strictly
   greater while a mobile toolbar is showing. That mismatch alone made `.page`
   overflow by the height of the toolbar, so every page had a short phantom scroll
   with nothing in it. `100%` resolves against the scroller itself and cannot.

   flex-shrink:0 is what lets the shell be TALLER than the scroller when the page
   is long. `.page` is `display:flex; flex-direction:column; height:100dvh`, so the
   shell is a flex item with the default `flex-shrink:1` — flexbox therefore shrank
   its BOX to the 812px container while its content (2273px on a catalog page)
   overflowed it. `min-height:100%` can't prevent that; it only sets the floor.
   A sticky child is clipped to its parent's box, so the context bar's sticky range
   ended at 812 - 71 = 741px and the bar scrolled away on every long page
   (measured: top -159px at scrollTop 900). With no shrinking the shell spans its
   content, the bar sticks for the whole page, and short pages are unaffected —
   min-height still pins them to exactly one viewport, so there is no phantom
   scroll. */
body.chapo-v4 .chapo-shell-v4 {
    min-height: 100%;
    flex-shrink: 0;
}

/* The v4 content element is a bare <main>, so it was picking up the legacy
   `body, .page, main, .content { background: var(--color-surface) }` rule from
   app.css — painting the content column WHITE while the shell and the sticky
   context bar painted `--chapo-bg`. That is the visible seam between the top bar
   and the page under it (measured: bar #fafaf8 vs content #ffffff on Catalog).
   Transparent lets the shell's background show through unbroken, so the bar
   matches whatever page is mounted instead of guessing at a fixed colour. */
body.chapo-v4 main.chapo-shell-v4-content {
    background: transparent;
}

/* Same reason the bar should not paint its own colour: inherit the shell's. */
body.chapo-v4 .chapo-ctxbar-v4-wrap {
    background: inherit;
}

/* ---------- 1. Buttons ---------------------------------------------------- */
body.chapo-v4 .btn {
    border-radius: var(--chapo-radius-pill) !important;
    padding: 0.55rem 1.1rem !important;
    font-weight: 600;
    font-size: var(--chapo-text-sm);
    transition: transform var(--chapo-duration-fast) var(--chapo-ease),
                background var(--chapo-duration-fast) var(--chapo-ease);
    border-width: 1px;
}
body.chapo-v4 .btn:active { transform: scale(0.97); }
body.chapo-v4 .btn-sm { padding: 0.35rem 0.85rem !important; font-size: var(--chapo-text-xs); }
body.chapo-v4 .btn-lg { padding: 0.85rem 1.5rem !important; font-size: var(--chapo-text-base); }

body.chapo-v4 .btn-primary {
    background: var(--chapo-accent) !important;
    border-color: var(--chapo-accent) !important;
    color: #fff !important;
}
body.chapo-v4 .btn-primary:hover { background: var(--chapo-accent-ink) !important; }

body.chapo-v4 .btn-success {
    background: var(--chapo-accent) !important;
    border-color: var(--chapo-accent) !important;
}
body.chapo-v4 .btn-danger {
    background: var(--chapo-danger) !important;
    border-color: var(--chapo-danger) !important;
}
body.chapo-v4 .btn-secondary {
    background: var(--chapo-surface-2) !important;
    border-color: var(--chapo-border) !important;
    color: var(--chapo-text) !important;
}
body.chapo-v4 .btn-outline-primary,
body.chapo-v4 .btn-outline-secondary,
body.chapo-v4 .btn-outline-dark,
body.chapo-v4 .btn-outline-danger {
    background: transparent !important;
    color: var(--chapo-text) !important;
    border-color: var(--chapo-border-strong) !important;
}
body.chapo-v4 .btn-outline-primary:hover,
body.chapo-v4 .btn-outline-secondary:hover,
body.chapo-v4 .btn-outline-dark:hover {
    background: var(--chapo-surface-2) !important;
    border-color: var(--chapo-accent) !important;
    color: var(--chapo-accent-ink) !important;
}
body.chapo-v4 .btn-outline-danger:hover {
    background: var(--chapo-warn-soft) !important;
    border-color: var(--chapo-danger) !important;
    color: var(--chapo-danger) !important;
}
body.chapo-v4 .btn-link {
    text-decoration: none !important;
    color: var(--chapo-accent-ink) !important;
    font-weight: 600;
}
body.chapo-v4 .btn-close { border-radius: 50% !important; }

/* ---------- 2. Cards ------------------------------------------------------ */
body.chapo-v4 .card {
    border-radius: var(--chapo-radius-lg) !important;
    border: 1px solid var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
    box-shadow: var(--chapo-shadow-sm);
    overflow: hidden;
}
body.chapo-v4 .card-header {
    background: transparent !important;
    border-bottom: 1px solid var(--chapo-border) !important;
    padding: var(--chapo-space-3) var(--chapo-space-4) !important;
    font-weight: 600;
}
body.chapo-v4 .card-body { padding: var(--chapo-space-4) !important; }
body.chapo-v4 .card-footer {
    background: transparent !important;
    border-top: 1px solid var(--chapo-border) !important;
}

/* ---------- 3. Forms ------------------------------------------------------ */
body.chapo-v4 .form-control,
body.chapo-v4 .form-select {
    border-radius: var(--chapo-radius-md) !important;
    border: 1px solid var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
    color: var(--chapo-text) !important;
    padding: 0.65rem 0.9rem !important;
    min-height: 44px;
}
body.chapo-v4 .form-control-lg,
body.chapo-v4 .form-select-lg { min-height: 52px; font-size: var(--chapo-text-base); }
body.chapo-v4 .form-control:focus,
body.chapo-v4 .form-select:focus {
    border-color: var(--chapo-accent) !important;
    box-shadow: 0 0 0 3px var(--chapo-accent-soft) !important;
    outline: none !important;
}
body.chapo-v4 .form-label { font-weight: 600; font-size: var(--chapo-text-sm); }
body.chapo-v4 .input-group { border-radius: var(--chapo-radius-md) !important; overflow: hidden; }
body.chapo-v4 .input-group > .form-control { border-radius: 0 !important; }
body.chapo-v4 .input-group-text {
    background: var(--chapo-surface-2) !important;
    border: 1px solid var(--chapo-border) !important;
    color: var(--chapo-text-2) !important;
}
body.chapo-v4 .form-check-input {
    border-radius: 6px !important;
    border: 1.5px solid var(--chapo-border-strong) !important;
    width: 18px; height: 18px;
}
body.chapo-v4 .form-check-input:checked {
    background-color: var(--chapo-accent) !important;
    border-color: var(--chapo-accent) !important;
}
body.chapo-v4 .form-switch .form-check-input { border-radius: var(--chapo-radius-pill) !important; width: 38px; height: 22px; }

/* ---------- 4. Modals (the big one — covers ALL popups) ------------------ */
body.chapo-v4 .modal-content {
    border-radius: var(--chapo-radius-xl) !important;
    border: 1px solid var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
}
body.chapo-v4 .modal-header {
    border-bottom: 1px solid var(--chapo-border) !important;
    padding: var(--chapo-space-4) !important;
    background: transparent !important;
}
body.chapo-v4 .modal-title { font-weight: 700; font-size: var(--chapo-text-lg); color: var(--chapo-text); }
body.chapo-v4 .modal-body { padding: var(--chapo-space-4) !important; color: var(--chapo-text); }
body.chapo-v4 .modal-footer {
    border-top: 1px solid var(--chapo-border) !important;
    padding: var(--chapo-space-3) var(--chapo-space-4) !important;
    gap: var(--chapo-space-2);
}
/* Bottom-sheet feel on phones — and on touch tablets, which get the mobile layout
   (see app.css "Layout mode"); the tablet width cap follows this block. */
@media (max-width: 576px), (hover: none) {
    /* `margin: 0` drops the `margin: .5rem auto` that app.css (§Modals, max-width 767.98px)
       uses to centre the dialog — but that rule ALSO sets `max-width: calc(100vw - 1rem)`,
       which still applies here. A block that is 1rem narrower than its container with no auto
       margins aligns to the inline-start edge, and in RTL that edge is the RIGHT, so the whole
       1rem of slack collected on the left and the dialog sat visibly off-centre (measured on a
       Pixel 7: 42px of gap on the left, 0 on the right).
       Going genuinely full-width is what the bottom-sheet look wants anyway, and it matches the
       v4 sheets (basket, checkout) that are edge-to-edge already. */
    body.chapo-v4 .modal-dialog-centered {
        align-items: flex-end;
        min-height: 100%;
        margin: 0;
        max-width: none;
        width: 100%;
    }
    body.chapo-v4 .modal-content {
        border-end-start-radius: 0 !important;
        border-end-end-radius: 0 !important;
        border-block-end: 0 !important;
        /* Keep the bottom sheet clear of BOTH safe areas. app.css caps the height at
           100vh - 1rem, which ignores the top inset — so a tall product-detail modal grew
           its top edge UP under the notch / Dynamic Island and its header (with the × close
           button) became untappable. Capping against the top inset is enough on its own:
           the sheet is bottom-anchored (align-items: flex-end above), so at full height its
           TOP edge lands exactly at inset-top + space-3, i.e. just below the island — no
           extra header padding needed. padding-bottom lifts the sheet's last control (the
           add-to-list button) above the home indicator. All env() insets are 0 off-iOS, so
           this is a no-op on web/Android; dvh keeps the math right under the iOS URL bar. */
        max-height: calc(100dvh - env(safe-area-inset-top, 0px) - var(--chapo-space-3)) !important;
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }
}
/* A touch tablet keeps the sheet, at the v4 sheets' width (V4Modal: 560px)
   instead of edge to edge across a 1280px screen. */
@media (min-width: 576.02px) and (hover: none) {
    body.chapo-v4 .modal-dialog-centered {
        max-width: 560px;
        margin-inline: auto;
    }
}

/* ---------- 5. Toasts ----------------------------------------------------- */
body.chapo-v4 .toast-container {
    position: fixed;
    inset-block-end: calc(var(--chapo-bottom-tabs-height) + var(--chapo-space-3));
    inset-inline: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--chapo-space-2);
    z-index: 3000;
    pointer-events: none;
    padding-inline: var(--chapo-space-4);
}
body.chapo-v4 .toast-item {
    display: inline-flex;
    align-items: center;
    gap: var(--chapo-space-2);
    background: var(--chapo-text);
    color: var(--chapo-bg);
    padding: var(--chapo-space-3) var(--chapo-space-4);
    border-radius: var(--chapo-radius-pill);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
    font-size: var(--chapo-text-sm);
    font-weight: 500;
    max-width: 90vw;
    animation: chapo-toast-in var(--chapo-duration-base) var(--chapo-ease);
}
body.chapo-v4 .toast-item.toast-exit { opacity: 0; transform: translateY(8px); transition: all var(--chapo-duration-base) var(--chapo-ease); }
@keyframes chapo-toast-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }

/* ---------- 6. Alerts ----------------------------------------------------- */
body.chapo-v4 .alert {
    border-radius: var(--chapo-radius-md) !important;
    border-width: 0 !important;
    border-inline-start: 3px solid var(--chapo-text) !important;
    font-size: var(--chapo-text-sm);
}
body.chapo-v4 .alert-success { background: var(--chapo-accent-soft) !important; border-inline-start-color: var(--chapo-accent) !important; color: var(--chapo-accent-ink) !important; }
body.chapo-v4 .alert-warning { background: var(--chapo-warn-soft) !important; border-inline-start-color: var(--chapo-warn) !important; }
body.chapo-v4 .alert-danger  { background: var(--chapo-warn-soft) !important; border-inline-start-color: var(--chapo-danger) !important; color: var(--chapo-danger) !important; }
body.chapo-v4 .alert-info    { background: var(--chapo-surface-2) !important; border-inline-start-color: var(--chapo-accent) !important; }

/* ---------- 7. Badges ----------------------------------------------------- */
body.chapo-v4 .badge {
    border-radius: var(--chapo-radius-pill) !important;
    padding: 0.25em 0.7em !important;
    font-weight: 600;
}
body.chapo-v4 .badge.bg-primary,
body.chapo-v4 .badge.bg-success { background: var(--chapo-accent) !important; color: #fff !important; }
body.chapo-v4 .badge.bg-warning { background: var(--chapo-warn) !important; color: #fff !important; }
body.chapo-v4 .badge.bg-danger  { background: var(--chapo-danger) !important; color: #fff !important; }
body.chapo-v4 .badge.bg-secondary { background: var(--chapo-surface-2) !important; color: var(--chapo-text-2) !important; }

/* ---------- 8. Lists ------------------------------------------------------ */
body.chapo-v4 .list-group { border-radius: var(--chapo-radius-md) !important; border: 1px solid var(--chapo-border) !important; overflow: hidden; }
body.chapo-v4 .list-group-item {
    background: var(--chapo-surface) !important;
    border-color: var(--chapo-border) !important;
    border-inline: 0 !important;
    padding: var(--chapo-space-3) var(--chapo-space-4) !important;
    color: var(--chapo-text);
}
body.chapo-v4 .list-group-item-action:hover { background: var(--chapo-surface-2) !important; }
body.chapo-v4 .list-group-item.active { background: var(--chapo-accent) !important; border-color: var(--chapo-accent) !important; color: #fff !important; }

/* ---------- 9. Tables ----------------------------------------------------- */
body.chapo-v4 .table {
    border-collapse: separate; border-spacing: 0;
    background: var(--chapo-surface);
    border-radius: var(--chapo-radius-md); overflow: hidden;
    border: 1px solid var(--chapo-border);
    color: var(--chapo-text);
}
body.chapo-v4 .table th { background: var(--chapo-bg) !important; font-weight: 600 !important; border-bottom: 1px solid var(--chapo-border) !important; }
body.chapo-v4 .table td { border-bottom: 1px solid var(--chapo-border) !important; }

/* ---------- 10. Dropdowns / menus ---------------------------------------- */
body.chapo-v4 .dropdown-menu {
    border-radius: var(--chapo-radius-md) !important;
    border: 1px solid var(--chapo-border) !important;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10);
    padding: var(--chapo-space-2) !important;
    background: var(--chapo-surface) !important;
}
body.chapo-v4 .dropdown-item { border-radius: var(--chapo-radius-sm); padding: 0.5rem 0.75rem !important; color: var(--chapo-text); }
body.chapo-v4 .dropdown-item:hover { background: var(--chapo-surface-2) !important; }

/* ---------- 11. Nav (in-page tabs/pills) --------------------------------- */
body.chapo-v4 .nav-tabs { border-bottom: 1px solid var(--chapo-border) !important; }
body.chapo-v4 .nav-tabs .nav-link {
    border: 0 !important; border-bottom: 2px solid transparent !important;
    border-radius: 0 !important; color: var(--chapo-text-3) !important;
    font-weight: 600; font-size: var(--chapo-text-sm);
}
body.chapo-v4 .nav-tabs .nav-link.active {
    color: var(--chapo-text) !important;
    border-bottom-color: var(--chapo-accent) !important;
    background: transparent !important;
}
body.chapo-v4 .nav-pills .nav-link { border-radius: var(--chapo-radius-pill) !important; color: var(--chapo-text) !important; font-weight: 600; }
body.chapo-v4 .nav-pills .nav-link.active { background: var(--chapo-accent-soft) !important; color: var(--chapo-accent-ink) !important; }

/* ---------- 12. Spinners -------------------------------------------------- */
body.chapo-v4 .spinner-border,
body.chapo-v4 .spinner-grow { color: var(--chapo-accent) !important; }

/* ---------- 13. Typography ----------------------------------------------- */
body.chapo-v4 h1, body.chapo-v4 .h1 { font-weight: 700; letter-spacing: -0.01em; }
body.chapo-v4 h2, body.chapo-v4 .h2 { font-weight: 700; letter-spacing: -0.01em; }
body.chapo-v4 h3, body.chapo-v4 .h3 { font-weight: 700; }
body.chapo-v4 .text-muted { color: var(--chapo-text-3) !important; }
body.chapo-v4 hr { border-top: 1px solid var(--chapo-border); opacity: 1; }

/* ---------- 14. App-specific containers ---------------------------------- */
/* ShopHealth shop cards */
body.chapo-v4 .shop-card,
body.chapo-v4 .shop-toggle {
    border-radius: var(--chapo-radius-lg) !important;
    border: 1px solid var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
}
body.chapo-v4 .shop-toggle.selected { border-color: var(--chapo-accent) !important; background: var(--chapo-accent-soft) !important; }

/* GroceryList product cards + catalog.
   The catalog grid's own card is <ProductCardV4>, which is token-native and needs
   no remap here — see Components/V4/ProductCardV4.razor.css. */
body.chapo-v4 .product-card,
body.chapo-v4 .grocery-item-card,
body.chapo-v4 .previous-list-card {
    border-radius: var(--chapo-radius-lg) !important;
    border: 1px solid var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
    transition: transform var(--chapo-duration-fast) var(--chapo-ease), box-shadow var(--chapo-duration-fast) var(--chapo-ease);
}
body.chapo-v4 .product-card:hover { transform: translateY(-1px); box-shadow: var(--chapo-shadow); }

/* Product-detail + order-detail dialogs (already .modal-content, refined) */
body.chapo-v4 .product-detail-modal,
body.chapo-v4 .order-detail-dialog .modal-content { border-radius: var(--chapo-radius-xl) !important; }

/* AiReport sections */
body.chapo-v4 .ai-report-block,
body.chapo-v4 .report-section,
body.chapo-v4 .ai-report-card {
    border-radius: var(--chapo-radius-lg) !important;
    border: 1px solid var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
}

/* Home store-result + cheapest-cart cards */
body.chapo-v4 .store-result-card,
body.chapo-v4 .cheapest-cart-card,
body.chapo-v4 .product-overview-card,
body.chapo-v4 .discount-section-card {
    border-radius: var(--chapo-radius-lg) !important;
    border: 1px solid var(--chapo-border) !important;
}

/* Chat / agent bubbles if present */
body.chapo-v4 .chat-bubble,
body.chapo-v4 .message-bubble {
    border-radius: var(--chapo-radius-lg);
    border: 1px solid var(--chapo-border);
    background: var(--chapo-surface);
}
body.chapo-v4 .chat-bubble.user,
body.chapo-v4 .chat-bubble-user {
    background: var(--chapo-accent) !important;
    color: #fff !important;
    border-color: var(--chapo-accent) !important;
}

/* ---------- 15. Hide the legacy chrome under the v4 shell ----------------- */
body.chapo-v4 .bottom-nav,
body.chapo-v4 .top-row,
body.chapo-v4 .mobile-header,
body.chapo-v4 .sidebar {
    display: none !important;
}

/* ---------- 16. Login page ------------------------------------------------ */
body.chapo-v4 .card.shadow-lg { box-shadow: 0 12px 40px rgba(0, 0, 0, 0.10) !important; }

/* ---------- 17. Legacy accent remap (blue → v4 green) --------------------- */
/* The legacy pages (Catalog/Settings/Login/ShopHealth/AiReport) are themed with
   the old --color-primary blue. Remap those vars onto the v4 accent so every
   legacy primary surface reads in the v4 green and matches the bespoke pages. */
body.chapo-v4 {
    --color-primary: var(--chapo-accent);
    --color-primary-hover: var(--chapo-accent-ink);
    --color-primary-subtle: var(--chapo-accent-soft);
    --color-primary-shadow: oklch(0.55 0.14 155 / 0.22);
}
/* app.css sets its palette on BOTH `:root` and the [data-bs-theme] wrapper, and
   MainLayout stamps that attribute on the .page div — a closer ancestor than
   body, so it wins the custom-property cascade over the block above and the
   legacy pages keep the stock palette. Re-assert the mapping at that same scope.

   Both themes, deliberately: app.css is slate/blue-tinted on either side
   (#2563eb primary and #1e293b text in light, #374151 borders and blue CTA
   gradients in dark), which makes the legacy Catalog/Settings pages read
   "blue" next to the warm v4 Home/Compare pages — and paints the brand
   lockups that inherit --color-primary blue instead of the app's green.
   This used to cover "dark" only, on the theory that light mode's neutral
   whites already blended in; they do, but its blue primary and slate-blue
   text do not. The --chapo-* tokens flip with the theme, so one mapping at
   [data-bs-theme] (any value) serves both. */
body.chapo-v4[data-bs-theme],
body.chapo-v4 [data-bs-theme] {
    --color-primary: var(--chapo-accent);
    --color-primary-hover: var(--chapo-accent-ink);
    --color-primary-subtle: var(--chapo-accent-soft);
    --color-primary-shadow: oklch(0.55 0.14 155 / 0.22);

    --color-surface: var(--chapo-bg);
    --color-surface-elevated: var(--chapo-surface);
    --color-surface-muted: var(--chapo-surface-2);
    --color-border: var(--chapo-border);
    --color-border-strong: var(--chapo-border-strong);
    --color-card-bg: var(--chapo-surface);
    --color-card-border: var(--chapo-border);
    --color-input-bg: var(--chapo-surface);
    --color-input-border: var(--chapo-border);
    --color-modal-bg: var(--chapo-surface);
    --color-text-primary: var(--chapo-text);
    --color-text-secondary: var(--chapo-text-2);
    --color-text-muted: var(--chapo-text-3);
    --color-link: var(--chapo-accent);
    --color-link-hover: var(--chapo-accent-ink);
    --spinner-active-color: var(--chapo-accent);

    /* Catalog footer "compare prices" CTA panel + button (blue gradients). */
    --cta-bg: linear-gradient(135deg, var(--chapo-accent-soft) 0%, var(--chapo-surface) 100%);
    --cta-border: var(--chapo-border);
    --cta-btn-bg: var(--chapo-accent);
    --cta-btn-shadow: oklch(0.55 0.14 155 / 0.35);
    --cta-btn-glow: oklch(0.55 0.14 155 / 0.18);
}

/* ---------- 18. Catalog (GroceryList) — pull into the v4 system ----------- */
body.chapo-v4 .grocery-page-wrapper { background: var(--chapo-bg); }

/* Category pill tabs (parent / child / filter) */
body.chapo-v4 .catalog-tab {
    border-radius: var(--chapo-radius-pill) !important;
    background: var(--chapo-surface) !important;
    border: 1px solid var(--chapo-border) !important;
    color: var(--chapo-text-2) !important;
    font-weight: 600;
    box-shadow: none !important;
}
/* Soft accent fill, no glow — the app-wide selection language. This row sits
   directly beside the catalog's mid-category chips, so a solid-accent pill with
   a green halo made two adjacent rows disagree about what "selected" looks
   like, and borrowed the colour that means "action" elsewhere. */
body.chapo-v4 .catalog-tab.active {
    background: var(--chapo-accent-soft) !important;
    border-color: transparent !important;
    color: var(--chapo-accent-ink) !important;
    box-shadow: none !important;
}

/* Desktop category sidebar */
body.chapo-v4 .catalog-nav { background: transparent !important; }
body.chapo-v4 .catalog-nav-item {
    border-radius: var(--chapo-radius-md) !important;
    color: var(--chapo-text-2) !important;
}
body.chapo-v4 .catalog-nav-item.active {
    background: var(--chapo-accent-soft) !important;
    color: var(--chapo-accent-ink) !important;
}

/* Search input + sort select */
body.chapo-v4 .catalog-search-input,
body.chapo-v4 .catalog-search-input .form-control,
body.chapo-v4 .catalog-sort-select {
    border-radius: var(--chapo-radius-md) !important;
    border-color: var(--chapo-border) !important;
    background: var(--chapo-surface) !important;
    color: var(--chapo-text) !important;
}

/* Sticky "Grocery List" footer bar + its CTA */
body.chapo-v4 .catalog-footer-inner {
    border-radius: var(--chapo-radius-xl) var(--chapo-radius-xl) 0 0 !important;
    background: var(--chapo-surface) !important;
    border: 1px solid var(--chapo-border) !important;
}
body.chapo-v4 .refresh-cta-button {
    background: var(--chapo-accent) !important;
    border: 0 !important;
    border-radius: var(--chapo-radius-pill) !important;
    color: #fff !important;
    box-shadow: 0 4px 16px oklch(0.55 0.14 155 / 0.3) !important;
}


/* ---------- 19. Shared v4 modal utilities (buttons + spinner) ------------ */
body.chapo-v4 .chapo-btn-primary { border: 0; background: var(--chapo-accent); color: #fff; font-weight: 600; padding: 0.6rem 1.2rem; border-radius: var(--chapo-radius-pill); cursor: pointer; }
body.chapo-v4 .chapo-btn-primary:hover { background: var(--chapo-accent-ink); }
body.chapo-v4 .chapo-btn-primary:disabled { opacity: 0.6; }
body.chapo-v4 .chapo-btn-ghost { border: 1px solid var(--chapo-border-strong); background: var(--chapo-surface); color: var(--chapo-text); font-weight: 600; padding: 0.6rem 1.2rem; border-radius: var(--chapo-radius-pill); cursor: pointer; }
body.chapo-v4 .chapo-spinner { width: 18px; height: 18px; border: 2px solid var(--chapo-border); border-top-color: var(--chapo-accent); border-radius: 50%; display: inline-block; animation: chapo-spin 0.8s linear infinite; vertical-align: middle; }

/* ---------- End ---------------------------------------------------------- */

/* ---------- 19. Image loading skeleton (cross-page utility) ----------------
   Add `chapo-imgskel` to an image WRAPPER; the <img> inside must call
   onload/onerror to add `is-imgloaded` (which hides the shimmer). Smooths the
   "image pops in after a delay" feeling on home / basket / compare / orders. */
.chapo-imgskel { position: relative; overflow: hidden; }
.chapo-imgskel::after {
    content: ""; position: absolute; inset: 0; z-index: 2; border-radius: inherit; pointer-events: none;
    background: linear-gradient(90deg, var(--chapo-surface-2), var(--chapo-surface), var(--chapo-surface-2));
    background-size: 200% 100%;
    animation: chapo-skeleton-shimmer 1.2s linear infinite;
}
.chapo-imgskel.is-imgloaded::after { display: none; }

/* ---------- 20. Desktop layout (≥992px): side nav rail + wide content -------
   The v4 shell is mobile-first: a sticky context bar, a narrow centered content
   column, and a fixed bottom tab bar. That is perfect on a phone but wastes a
   desktop screen. Everything in this section is gated behind `min-width: 992px`
   AND `hover: hover`, so at ≤991px — and on any touch-first device (tablets, the
   native app) at any width — the mobile layout is untouched (app.css "Layout mode").

   On desktop the already-fixed bottom tab bar is re-anchored into a vertical
   side rail on the INLINE-START edge (right in Hebrew/RTL, left in LTR), the
   content column widens, and the viewport-fixed bars that used to sit above the
   bottom tab bar (toasts, the catalog footer) are re-cleared of the rail.

   992px (not 1024) is chosen to match the legacy catalog page, which already
   switches to its own desktop layout + fixed-footer offset at 992px — aligning
   avoids a 992–1023px window where the catalog goes desktop but the rail wouldn't.

   The tab <a>s are NavLink-rendered and do NOT carry BottomTabsV4's CSS-isolation
   scope attribute, so this global, body.chapo-v4-scoped sheet can style them with
   plain class selectors (no ::deep needed — that only works inside the component). */
@media (min-width: 992px) and (hover: hover) {

    /* --- Bottom tab bar → vertical side navigation rail --- */
    body.chapo-v4 .chapo-tabs-v4 {
        inset-block: 0;                      /* top:0 + bottom:0 → full-height rail */
        inset-inline-start: 0;               /* was: inset-inline:0; bottom:0 (mobile) */
        inset-inline-end: auto;
        width: var(--chapo-sidebar-width);
        display: flex;                       /* was: an auto-flow grid (mobile) */
        flex-direction: column;
        align-items: stretch;
        justify-content: flex-start;
        gap: 4px;
        padding: var(--chapo-space-4) var(--chapo-space-3);
        border-top: 0;
        border-inline-end: 1px solid var(--chapo-border);
        overflow-y: auto;                    /* guard against very short viewports */
    }
    /* Tab items: horizontal icon + label rows instead of stacked cells.
       The mobile rule `.chapo-tabs-v4 ::deep .chapo-tab-v4` compiles to specificity
       (0,3,0) — it includes the scope attribute — so these overrides MUST keep the
       `.chapo-tabs-v4` ancestor to out-specify it (0,3,1); without it the items stay
       icon-over-label (flex-direction:column) on desktop. */
    body.chapo-v4 .chapo-tabs-v4 .chapo-tab-v4 {
        flex-direction: row;
        justify-content: flex-start;
        gap: var(--chapo-space-3);
        padding: 10px 12px;
        border-radius: var(--chapo-radius-md);
        font-size: var(--chapo-text-sm);
        font-weight: 500;
        line-height: 1.2;
        text-align: start;
    }
    body.chapo-v4 .chapo-tabs-v4 .chapo-tab-v4 i { font-size: 1.25rem; }
    body.chapo-v4 .chapo-tabs-v4 .chapo-tab-v4:hover { background: var(--chapo-surface-2); color: var(--chapo-text-2); }
    body.chapo-v4 .chapo-tabs-v4 .chapo-tab-v4.active { background: var(--chapo-accent-soft); color: var(--chapo-accent-ink); font-weight: 600; }
    body.chapo-v4 .chapo-tabs-v4 .chapo-tab-v4.active i { color: var(--chapo-accent-ink); }

    /* Brand header — style the wordmark in green "ink" with the app font (Heebo in
       RTL), mirroring auth.css .auth-app-name.
       NOTE: this used to carry `filter: hue-rotate(-75deg)` on the img to fake green
       from the old *blue* app-icon.png. #134 shipped a genuinely green icon, so that
       filter was rotating the brand green AWAY from brand — leaving the rail icon a
       different color from the same asset in the About dialog. Deliberately no filter:
       app-icon.png is already the brand green and must render untouched everywhere. */
    body.chapo-v4 .chapo-tabs-v4-brand {
        color: var(--chapo-accent-ink);
        font-family: var(--chapo-font-sans);
    }
    body.chapo-v4 .chapo-tabs-v4[dir="rtl"] .chapo-tabs-v4-brand { font-family: var(--chapo-font-heb); }

    /* --- Push all shell content clear of the fixed rail, and widen it ---
       Overriding the var on this ancestor cascades to the content <main> AND to
       every v4 page root (they all read `max-width: var(--chapo-content-max-width)`),
       so all pages widen at once with no per-page edit. Custom-property inheritance
       is unaffected by Blazor CSS isolation — the same trick §17 uses for theming. */
    body.chapo-v4 .chapo-shell-v4 {
        /* .page is display:flex; flex-direction:row on desktop (a legacy rule for
           the old sidebar layout). The v4 shell is its only in-flow flex child, so
           without flex-grow it shrink-wraps to content width. Make it fill so the
           content area is a consistent width regardless of how much each page renders. */
        flex: 1;
        min-width: 0;
        padding-inline-start: var(--chapo-sidebar-width);
        --chapo-content-max-width: 1120px;
    }
    /* No bottom tab bar on desktop → drop its space reservation */
    body.chapo-v4 .chapo-shell-v4-content {
        padding-block-end: var(--chapo-space-6);
    }
    /* Keep the context bar + inner-page back header aligned to the content column
       (both are siblings of the content <main>, so otherwise they'd run edge-to-edge) */
    body.chapo-v4 .chapo-ctxbar-v4,
    body.chapo-v4 .chapo-shell-back {
        /* width:100% is required: these are flex items in the column shell, and
           margin-inline:auto alone would disable the cross-axis stretch and make
           them shrink-wrap to content. width:100% fills the content area; the
           max-width + auto margins then cap and center on very wide screens. */
        width: 100%;
        max-width: var(--chapo-content-max-width);
        margin-inline: auto;
    }

    /* --- Re-clear the viewport-fixed bars of the rail --- */
    /* Toasts are rendered OUTSIDE the shell (no rail ancestor), so anchor explicitly */
    body.chapo-v4 .toast-container {
        inset-block-end: var(--chapo-space-5);
        inset-inline-start: var(--chapo-sidebar-width);
        inset-inline-end: 0;
    }
    /* Legacy catalog's own fixed footer has a min-width:992px offset for ITS OWN
       in-page category nav and knows nothing about the app rail. Clear the rail and
       re-center the inner over the content. The extra `.catalog-footer` in the
       selector lifts specificity to (0,3,1) so it beats the catalog's own
       `:global([dir]) .catalog-footer-inner` (0,3,0) margin offsets. */
    body.chapo-v4 .catalog-footer {
        inset-inline-start: var(--chapo-sidebar-width);
        inset-inline-end: 0;
    }
    body.chapo-v4 .catalog-footer .catalog-footer-inner {
        width: min(calc(100% - 2 * var(--chapo-space-6)), var(--chapo-content-max-width));
        margin-inline: auto;
    }

    /* Modals are viewport-fixed too, so they centre over the WHOLE viewport while the page
       content is inset by the rail — the dialog then sits about half a rail-width off-centre
       against the content behind it. Pad the overlay (not the dialog) so its centring box
       matches the content column; the scrim still covers the full viewport, rail included.
       Bootstrap centres .modal-dialog with `margin: … auto` and V4Modal centres with
       `justify-content: center`, so both follow the padding automatically. */
    body.chapo-v4 .modal {
        padding-inline-start: var(--chapo-sidebar-width);
    }
    /* V4Modal's backdrop already carries a space-4 gutter at ≥600px — keep it. */
    body.chapo-v4 .chapo-modal-backdrop {
        padding-inline-start: calc(var(--chapo-sidebar-width) + var(--chapo-space-4));
    }
}

/* ---------- 20b. Touch tablets: the mobile layout, using the room -----------
   Tablets (and the native app on one) get the phone layout — bottom tabs, sheets,
   the catalog's department tabs — at every width (app.css "Layout mode"). Only the
   amount of room changes. Portrait (768–991px) already fills the screen at the
   phone column width. In landscape:
   - the catalog widens to the desktop's 1120px, so its grids gain columns instead
     of two stretched cards floating in a 760px strip;
   - every other page (compare, orders, account, basket) is a single reading column
     and keeps the phone width — the same reason desktop caps compare and account;
   - the top bar and the inner-page back header line up with that column rather
     than running edge to edge above it (as on desktop, §20). */
@media (min-width: 992px) and (hover: none) {
    body.chapo-v4 .chapo-shell-v4.is-catalog {
        --chapo-content-max-width: 1120px;
    }
    body.chapo-v4 .chapo-ctxbar-v4,
    body.chapo-v4 .chapo-shell-back {
        width: 100%;
        max-width: var(--chapo-content-max-width);
        margin-inline: auto;
    }
}

/* ---------- 20. Keyboard focus + press feedback, app-wide ----------------- */
/* A visible focus ring existed on the product card and the catalog only, so
   tabbing through the bottom tabs, the context bar, compare/tracking segments,
   the cart-sheet steppers and the account rows showed nothing at all. One rule
   covers every interactive element in the v4 shell; :focus-visible means pointer
   users never see it. */
body.chapo-v4 button:focus-visible,
body.chapo-v4 a:focus-visible,
body.chapo-v4 summary:focus-visible,
body.chapo-v4 [role="tab"]:focus-visible,
body.chapo-v4 [role="option"]:focus-visible,
body.chapo-v4 [tabindex]:not([tabindex="-1"]):focus-visible {
    outline: 2px solid var(--chapo-accent-ink);
    outline-offset: 2px;
    border-radius: var(--chapo-radius-sm);
}

/* Press feedback on the tappable rows that had none (store rows, order cards,
   account rows, cart-sheet steppers). Deliberately subtler than the card's
   0.92 — these are wide targets, where a large scale reads as a glitch. */
body.chapo-v4 .chapo-storerow:active,
body.chapo-v4 .chapo-ordercard:active,
body.chapo-v4 .chapo-acct-row:active,
body.chapo-v4 .chapo-cartsheet-qbtn:active,
body.chapo-v4 .chapo-trk-tf:active {
    transform: scale(0.985);
}

@media (prefers-reduced-motion: reduce) {
    body.chapo-v4 .chapo-storerow:active,
    body.chapo-v4 .chapo-ordercard:active,
    body.chapo-v4 .chapo-acct-row:active,
    body.chapo-v4 .chapo-cartsheet-qbtn:active,
    body.chapo-v4 .chapo-trk-tf:active { transform: none; }
}

/* ============================================================ */
/* Catalog: the page owns the top, the shell bar stands down     */
/* ============================================================ */
/* The catalog's header is a single row — search, filter and account — so the
   shell's context bar (mode picker + address) is hidden there entirely rather
   than fused with it. Address and delivery mode moved into the basket sheet,
   beside the compare button they actually affect.

   The WRAPPER stays in the flow with zero padding instead of being removed: it
   is what carries `top: env(safe-area-inset-top)`, and it is what
   syncContextBarHeight measures. Collapsed to nothing it still publishes the
   notch inset as --chapo-ctxbar-offset, so the catalog's own card pins directly
   under the status bar. Removing it would publish 0 and slide the card under the
   notch on a device. */
body.chapo-v4.chapo-catalog-head .chapo-ctxbar-v4 {
    display: none;
}
/* Keep the wrapper's TOP padding even with the card hidden. Collapsing it to
   zero is what left the catalog's header sitting higher than every other page's:
   elsewhere the card starts at inset + space-2 (the wrapper's own top padding),
   so with the wrapper collapsed the catalog started at inset + 0.
   Keeping it means the offset this publishes, and the in-flow position of the
   page's own card, are BOTH inset + space-2 — identical by construction instead
   of by adding a matching number somewhere else and hoping they stay in step. */
body.chapo-v4.chapo-catalog-head .chapo-ctxbar-v4-wrap {
    padding: var(--chapo-space-2) 0 0;
    box-shadow: none;
}
body.chapo-v4.chapo-catalog-head .chapo-ctxbar-v4-wrap::after {
    display: none;
}

/* ---- Shop channel badge ----
   Says whether a shop can be bought from without leaving the house. Needed once
   the "All shops" ranking mixes online and in-store shops in one list, where the
   cheapest row would otherwise look orderable when it is only shoppable in
   person. Used on Compare (winner card + every store row) and on Orders. */
body.chapo-v4 .chapo-shopbadge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-inline-start: var(--chapo-space-2);
    padding: 2px 9px;
    border-radius: var(--chapo-radius-pill);
    border: 1px solid transparent;
    font-size: var(--chapo-text-xs);
    font-weight: 700;
    line-height: 1.55;
    vertical-align: middle;
    white-space: nowrap;
}
/* An icon as well as a word. These two sit side by side in one ranked list and
   decide whether you can order or must travel, so they have to separate at a
   glance rather than on reading 11px Hebrew: different colour, different border,
   AND different glyph — a van versus a shopfront. */
body.chapo-v4 .chapo-shopbadge i { font-size: 0.85em; }
body.chapo-v4 .chapo-shopbadge.is-online {
    background: var(--chapo-accent-soft);
    border-color: color-mix(in oklab, var(--chapo-accent) 40%, transparent);
    color: var(--chapo-accent-ink);
}
/* Outlined and neutral, never red: buying in person is a normal option, just a
   different one, so it reads as a distinction rather than a problem. */
body.chapo-v4 .chapo-shopbadge.is-instore {
    background: transparent;
    border-color: var(--chapo-border-strong);
    color: var(--chapo-text-2);
}


/* ---- Segmented filter bar (Compare's shop modes, Orders' order kinds) ----
   Global rather than duplicated in each page's scoped sheet: the two rows sit in
   the same place on two sibling pages, so any difference between them reads as a
   mistake — and two hand-matched copies is exactly how that difference appears.
   The count sits UNDER its label, on the chip's own centre line: trailing the
   label it fell between two chips and read as belonging to whichever one the eye
   reached first, which in RTL is the wrong one. */
body.chapo-v4 .chapo-segbar {
    display: flex;
    gap: 4px;
    padding: 4px;
    background: var(--chapo-surface);
    /* A visible frame around the whole control. Background alone read as a label
       strip — with only the active chip filled, nothing said the other options
       were buttons at all. The border is what marks the row as one control whose
       parts are all live, the way a segmented control conventionally reads. */
    border: 1px solid var(--chapo-border-strong, var(--chapo-border));
    border-radius: var(--chapo-radius-pill);
    min-width: 0;
}
body.chapo-v4 .chapo-segbtn {
    flex: 1;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    border: 0;
    background: transparent;
    cursor: pointer;
    font-family: inherit;
    padding: 6px 10px;
    border-radius: var(--chapo-radius-pill);
    color: var(--chapo-text-2);
    font-size: var(--chapo-text-sm);
    font-weight: 600;
    min-width: 0;
}
/* Soft accent = current state; solid accent stays reserved for actions. */
body.chapo-v4 .chapo-segbtn.is-active {
    background: var(--chapo-accent-soft);
    color: var(--chapo-accent-ink);
}
body.chapo-v4 .chapo-segline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    max-width: 100%;
    min-width: 0;
}
body.chapo-v4 .chapo-segline > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
body.chapo-v4 .chapo-segcount {
    font-size: 0.6875rem;
    font-weight: 700;
    line-height: 1;
    opacity: 0.7;
    font-variant-numeric: tabular-nums;
}

/* ============================================================================
   Checkout replacements — SHARED, and global on purpose.

   These moved out of CompareV4.razor.css when the orders page began showing the
   same replacement picker before a re-buy. Blazor scopes a component stylesheet to
   that component, so the orders copy of this markup rendered completely unstyled
   while looking correct in the source. Two identical copies of the rules would have
   fixed the symptom and started the drift; one global definition is what actually
   makes the two surfaces stay the same surface.
   ========================================================================== */
/* ---- checkout replacements for live-missing items (stor.ai) ---- */
.chapo-checkout-alts {
    margin-top: var(--chapo-space-3);
    padding: var(--chapo-space-3);
    border: 1px solid color-mix(in oklab, var(--chapo-warn) 45%, var(--chapo-border));
    border-radius: var(--chapo-radius-lg);
    background: color-mix(in oklab, var(--chapo-warn) 8%, var(--chapo-surface));
}
/* What continuing would drop, named. Reads at DANGER weight, not warn: the block it
   sits in is already a warning about availability, and "these specific products will
   not arrive" is a stronger claim than "some things need attention" — at the same
   colour it disappeared into its own container. */
.chapo-checkout-alts-warn {
    display: flex;
    align-items: flex-start;
    gap: var(--chapo-space-2);
    margin-block-end: var(--chapo-space-3);
    padding: var(--chapo-space-2) var(--chapo-space-3);
    border-radius: var(--chapo-radius-md);
    border: 1px solid color-mix(in oklab, var(--chapo-danger) 45%, transparent);
    background: color-mix(in oklab, var(--chapo-danger) 10%, var(--chapo-surface));
}
.chapo-checkout-alts-warn > i {
    color: var(--chapo-danger);
    font-size: 1rem;
    line-height: 1.35;
    flex: 0 0 auto;
}
.chapo-checkout-alts-warn-title {
    font-weight: 700;
    font-size: var(--chapo-text-sm);
    color: var(--chapo-text);
}
/* The products themselves. A count alone is the thing that was already being missed,
   so the names carry the weight and the list is never collapsed behind a "show more". */
.chapo-checkout-alts-warn-list {
    margin: var(--chapo-space-1) 0 0;
    padding-inline-start: 1.1em;
    font-size: var(--chapo-text-sm);
    color: var(--chapo-text);
}
.chapo-checkout-alts-warn-list li { margin-block: 1px; }
.chapo-checkout-alts-warn-hint {
    margin-block-start: var(--chapo-space-2);
    font-size: var(--chapo-text-xs);
    color: var(--chapo-text-2);
}

/* One loading block for the whole replacements section. Sized to roughly what a finished
   card occupies so the sheet does not jump when the real ones arrive. */
.chapo-checkout-alts-loading { display: flex; flex-direction: column; gap: var(--chapo-space-3); }
.chapo-checkout-alts-loading-text {
    display: flex; align-items: center; gap: var(--chapo-space-2);
    color: var(--chapo-text-2); font-size: var(--chapo-text-sm);
}
.chapo-checkout-alts-loading-text .chapo-spinner { width: 16px; height: 16px; border-width: 2px; }
.chapo-checkout-alt-skel { display: flex; flex-direction: column; gap: var(--chapo-space-2); }
.chapo-checkout-alt-skel-row { height: 34px; border-radius: var(--chapo-radius-md); }
.chapo-checkout-alt-skel-opts { display: flex; gap: var(--chapo-space-2); }
.chapo-checkout-alt-skel-opts span {
    flex: 1 1 0; height: 44px; border-radius: var(--chapo-radius-md);
}

.chapo-checkout-alts-head {
    display: flex; align-items: flex-start; gap: 8px;
    color: var(--chapo-warn); font-weight: 600; font-size: var(--chapo-text-sm);
    margin-bottom: var(--chapo-space-2);
}
.chapo-checkout-alt { padding-top: var(--chapo-space-2); }
.chapo-checkout-alt + .chapo-checkout-alt { border-top: 1px dashed var(--chapo-border); margin-top: var(--chapo-space-2); }
/* The problem line (original item): breakdown-row anatomy — shared thumb classes,
   name, and a warn status chip where the breakdown puts the price. */
.chapo-checkout-alt-orig { display: flex; align-items: center; gap: var(--chapo-space-3); min-width: 0; }
.chapo-checkout-alt-name { flex: 1; min-width: 0; font-weight: 600; color: var(--chapo-text); font-size: var(--chapo-text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chapo-checkout-alt-status {
    flex: 0 0 auto;
    font-size: var(--chapo-text-xs); font-weight: 600;
    color: var(--chapo-warn);
    background: var(--chapo-surface);
    border: 1px solid var(--chapo-border);
    border-radius: var(--chapo-radius-pill);
    padding: 2px 10px;
}
.chapo-checkout-alt-note { display: flex; align-items: center; gap: var(--chapo-space-2); color: var(--chapo-text-3); font-size: var(--chapo-text-sm); padding: var(--chapo-space-2) 0; }
/* Candidate rows: same card anatomy as the swaps/store rows (surface + border +
   radius-lg, accent border + ring when selected) with a storerow-style circle check. */
.chapo-checkout-alt-opts { display: flex; flex-direction: column; gap: var(--chapo-space-2); margin-top: var(--chapo-space-2); }
.chapo-checkout-alt-opt {
    display: flex; align-items: center; gap: var(--chapo-space-3); width: 100%;
    padding: var(--chapo-space-2) var(--chapo-space-3);
    border: 1px solid var(--chapo-border); border-radius: var(--chapo-radius-lg);
    background: var(--chapo-surface); cursor: pointer; text-align: start;
    box-shadow: var(--chapo-shadow-sm);
    transition: border-color var(--chapo-duration-base) var(--chapo-ease),
                background var(--chapo-duration-base) var(--chapo-ease);
}
.chapo-checkout-alt-opt:hover { border-color: var(--chapo-accent); }
.chapo-checkout-alt-opt.is-selected {
    border-color: var(--chapo-accent);
    box-shadow: 0 0 0 1px var(--chapo-accent);
    background: var(--chapo-accent-soft);
}
[data-bs-theme="dark"] .chapo-checkout-alt-opt.is-selected { background: color-mix(in oklab, var(--chapo-accent) 22%, var(--chapo-surface)); }
.chapo-checkout-alt-opt-check {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px;
    color: var(--chapo-text-3); font-size: 1.3rem; line-height: 1;
}
.chapo-checkout-alt-opt.is-selected .chapo-checkout-alt-opt-check { color: var(--chapo-accent); }
/* TWO lines, not one clipped one. A single can and a six-pack of the same drink differ only in
   the tail of the name, so one nowrap line rendered them as the same string at two prices — the
   shopper sees "the same product twice" and cannot tell which is which. Clamped at two so a long
   name still cannot push the price out of the row. */
.chapo-checkout-alt-opt-name {
    flex: 1; min-width: 0; font-size: var(--chapo-text-sm); font-weight: 500; color: var(--chapo-text);
    white-space: normal; overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.25;
}
.chapo-checkout-alt-opt.is-selected .chapo-checkout-alt-opt-name { font-weight: 600; }
.chapo-checkout-alt-opt-price { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 700; color: var(--chapo-text); font-size: var(--chapo-text-sm); }

/* Product thumbnail in a line breakdown (matches the basket thumbnail treatment).
   Global for the same reason as the block above: the orders modal shows these rows too. */
/* Product thumbnail in the breakdown (matches the basket thumbnail treatment) */
.chapo-compare-breakdown-thumb {
    position: relative;
    flex: 0 0 auto;
    width: 34px; height: 34px;
    border-radius: var(--chapo-radius-sm);
    overflow: hidden;
    background: var(--chapo-surface-2);
}
.chapo-compare-breakdown-thumb img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;
    background: var(--chapo-card-image-bg);
}
.chapo-compare-breakdown-thumb-fb {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--chapo-text-3);
}
.chapo-compare-breakdown-thumb-fb i { font-size: 1rem; }

/* Checkout totals row — global, same reason as the picker above it: the orders
   re-buy sheet shows this exact block, and scoped to CompareV4 it rendered there
   as unstyled text. */
.chapo-checkout-line { display: flex; justify-content: space-between; padding: var(--chapo-space-2) 0; color: var(--chapo-text-2); border-top: 1px solid var(--chapo-border); }
.chapo-checkout-line strong { color: var(--chapo-text); font-variant-numeric: tabular-nums; }
.chapo-checkout-line.is-total { font-size: var(--chapo-text-lg); }
.chapo-checkout-line.is-total strong { color: var(--chapo-accent-ink); }
[data-bs-theme="dark"] .chapo-checkout-line.is-total strong { color: var(--chapo-accent); }
.chapo-checkout-line.is-adjust {
    color: var(--chapo-text-2);
    font-size: var(--chapo-text-sm);
}
.chapo-checkout-foottotal {
    display: flex;
    flex-direction: column;
}
.chapo-checkout-foottotal .chapo-checkout-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--chapo-space-3);
}
.chapo-checkout-foottotal .chapo-checkout-line.is-total {
    font-size: var(--chapo-text-lg);
    font-weight: 800;
    color: var(--chapo-text);
}
.chapo-checkout-foottotal .chapo-checkout-line.is-adjust {
    margin-block-start: 2px;
    font-size: var(--chapo-text-xs);
    font-weight: 600;
    color: var(--chapo-text-2);
}
