/* PRJ-006: Mobile Interactions Engine */
/* All rules gated on body.mobile-mode — zero effect on desktop */

/* Body locking for when drawers or zoom states are active */
body.mobile-mode.mobile-interaction-active {
    touch-action: pan-y;
}

/* ----------------------------------------------------------------------
   PANEL 1: Dashboard Hero Area
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .mobile-dashboard-hero {
        position: relative;
        z-index: 10;
        margin-bottom: 1rem;
        transition: transform 0.3s ease;
    }
}

/* ----------------------------------------------------------------------
   PANEL 2: Bottom-Sliding Input Drawers (Header, etc)
   ---------------------------------------------------------------------- */
body.mobile-mode .mobile-drawer-target {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 85vh;
    background: #ffffff;
    z-index: 1050;
    transform: translateY(110%);
    transition: transform 0.35s cubic-bezier(0.25, 1, 0.5, 1);
    border-top-left-radius: 20px;
    border-top-right-radius: 20px;
    box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.15);
    overflow-y: auto;
}

body.mobile-mode .mobile-drawer-target.drawer-open {
    transform: translateY(0);
}

body.mobile-mode .drawer-header {
    position: sticky;
    top: 0;
    z-index: 1060;
    border-top-left-radius: 20px;
    border-top-right-radius: 20px;
}

/* ----------------------------------------------------------------------
   PREMIUM INPUTS: Bronze Script & Gold Pinstripes
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .mobile-input-premium {
        border: none !important;
        border-bottom: 2px solid rgba(176, 141, 87, 0.3) !important;
        border-radius: 0 !important;
        padding-left: 0;
        padding-right: 0;
        background-color: transparent !important;
        font-family: inherit;
        transition: border-color 0.2s;
    }

    .mobile-input-premium:focus {
        box-shadow: none !important;
        border-bottom-color: #0f1c2d !important;
    }

    .bronze-script::placeholder {
        color: #b08d57;
        font-style: italic;
        font-family: 'Georgia', serif;
        opacity: 0.8;
    }
}

/* ----------------------------------------------------------------------
   PANEL 3: Nested Contexts (The Zoom Effect)
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .mobile-line-card {
        background: #ffffff;
        border-radius: 12px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
        margin-bottom: 1rem;
        padding: 1.2rem;
        display: block !important;
        border-left: 4px solid #0f1c2d;
        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
        cursor: pointer;
    }

    .mobile-line-card td {
        display: block;
        border: none;
        padding: 0.2rem 0;
    }

    .mobile-line-card td:first-child {
        font-weight: bold;
        color: #6c757d;
        font-size: 0.85rem;
    }

    .mobile-line-card td:nth-child(2) {
        font-size: 1.1rem;
        margin-bottom: 0.5rem;
    }

    .mobile-line-card td:nth-child(3):before {
        content: 'Qty: ';
        color: #6c757d;
        font-size: 0.85rem;
    }

    .mobile-line-card td:nth-child(4):before {
        content: 'Price: ';
        color: #6c757d;
        font-size: 0.85rem;
    }

    .mobile-line-card td:last-child {
        font-size: 1.2rem;
        font-weight: 700;
        color: #0f1c2d;
        text-align: left !important;
        margin-top: 0.5rem;
        border-top: 1px dashed #e9ecef;
        padding-top: 0.5rem;
    }

    .mobile-line-card td:last-child:before {
        content: 'Total: ';
        color: #6c757d;
        font-size: 0.85rem;
        font-weight: normal;
    }

    /* P12: Transaction edit — mobile table override
   Disables the Level 3 Shield (min-width, fixed layout, thead) on mobile
   where desktop rows are hidden and only card rows render. */
    #lines-table {
        min-width: 0 !important;
        table-layout: auto !important;
        border-collapse: collapse !important;
    }

    #lines-table thead {
        display: none !important;
    }

    #lines-table td {
        background-color: transparent !important;
        padding: 0 !important;
        border: none !important;
    }

    #lines-table tr.d-md-none {
        display: table-row !important;
    }

    /* P12: Transaction edit line item cards */
    .mobile-line-item-card {
        background: #f8f9fa !important;
        border-radius: 10px;
        border-left: 4px solid #0f1c2d;
        border-bottom: none !important;
        box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08);
        margin: 0.5rem 0;
        overflow: hidden;
        position: relative;


    }

    .mobile-line-item-card .mobile-card-inner {
        background: #f8f9fa !important;
        padding-top: 0.5em !important;
        padding-left: 0.5em !important;
    }

    .mobile-line-item-card .mobile-card-inner>div {
        background: #f8f9fa;
    }

    .mobile-line-item-card:active {
        transform: scale(0.98);
    }

    /* Unified cents treatment for ALL mobile money displays (sticky strip,
       line totals, total card): 0.8 × the whole-number size, aligned top.
       Each cents span's 0.8em must resolve against the same base as its
       sibling whole-number span — containers set that base (see
       .mobile-line-total below). Colour/opacity stay per-context via the
       modifier classes that follow. Mobile-only (this media query): the
       total card's split spans render unstyled/full-size on desktop. */
    .mobile-money-cents {
        font-family: 'Roboto Mono', monospace;
        font-weight: 600;
        font-size: 0.8em;
        vertical-align: top;
    }

    /* PRJ-031 B3: light-card line total (dark-strip equivalent is
       .mobile-doc-strip-money-* further down). The wrapper sets the em base
       for .mobile-money-cents; the whole span inherits it. */
    .mobile-line-total {
        font-size: 0.9rem;
    }

    .mobile-line-total-whole {
        font-family: 'Roboto Mono', monospace;
        font-weight: 700;
        color: #1a1a1a;
    }

    .mobile-line-total-cents {
        opacity: 0.65;
        color: #1a1a1a;
    }

    /* Bottom total card (Total Amount Due) */
    .amount-large-cents {
        opacity: 0.85;
    }

    /* Bugfix (post-B4): #add-line-form (master_editor()) only carries col-md-*
   classes — desktop-oriented, since its only reachable home used to be the
   wide "+ Add Line" collapse panel. Below 768px, Bootstrap applies none of
   those column widths (they're gated inside a min-width:768px media query),
   so each field renders at native/intrinsic width with nothing to wrap or
   stack it — the .row ends up far wider than the viewport, and everything
   past the first field is pushed off-screen to the right. Force every column
   to stack full-width on mobile, in both its original "+ Add Line" location
   and the new PRJ-031 post-B4 line-tap-to-edit slot. */
    #add-line-form .row>[class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
        /* Flex items default to min-width:auto, so a wide <select> (long item
       codes/descriptions) can still force this column past its 100% basis
       and blow out the row — min-width:0 lets it actually shrink/wrap. */
        min-width: 0;
    }

    #add-line-form select,
    #add-line-form input,
    #add-line-form textarea {
        max-width: 100%;
    }

    /* Same failure class as the col-* stacking fix above, one level deeper:
       the calculation preview (#line-summary-block) is an unwrapped desktop
       flex row (gap-5 / gap-4 / border-end pe-5) — on mobile it must wrap and
       drop the desktop-only divider/right-alignment. */
    #add-line-form #line-summary-block .card-body {
        flex-wrap: wrap;
        justify-content: flex-start;
        gap: 0.75rem !important; /* overrides gap-5 */
    }

    #add-line-form #line-summary-block .card-body > .border-end {
        border-right: none !important;
        padding-right: 0 !important;
        flex-wrap: wrap;
        gap: 0.5rem 1rem !important; /* overrides gap-4 */
    }

    #add-line-form #line-summary-block .d-flex.flex-column {
        min-width: 0; /* neutralize inline min-width:150px pull */
        align-items: flex-start !important;
    }

    /* Bugfix (post-B4): .mobile-card-inner is a shared display:flex row (see the
   P18 swipe-actions rule further down) — .mobile-line-edit-slot sits as a
   direct sibling of the summary content div inside it, so without wrapping it
   was laid out as a second flex item in the SAME row instead of stacking
   below, and flex items default to min-width:auto (won't shrink below their
   widest descendant), so the edit form pushed the whole row far past the
   viewport. Scoped to line-item cards only — .mobile-card-inner is reused
   elsewhere (list_view.html, _ledger_macros.html) where this doesn't apply. */
    .mobile-line-item-card .mobile-card-inner {
        flex-wrap: wrap;
    }

    .mobile-line-edit-slot {
        flex: 1 1 100%;
        width: 100%;
        min-width: 0;
    }

    /* When editing a line on mobile, the sticky Save Line/Cancel footer
   (.zoom-action-footer .footer-line-actions) is the single source of truth
   for committing/discarding the edit — the form's own embedded submit/cancel
   controls stay functional (the footer buttons trigger them via .click())
   but must not also be visible, or the card shows two competing action rows.
   Scoped to the mobile edit slot only — untouched in the form's other homes
   (desktop, and the mobile "+ Add Line" collapse panel). */
    .mobile-line-edit-slot .line-form-actions-col {
        display: none;
    }

    /* PRJ-031: mobile Add Line uses the zoom popup exclusively — hide the desktop
   concertina panel below 768px (its trigger is already d-none d-md-flex). This
   also neutralizes the zero-lines case where Jinja's show_editor auto-opens it. */
    #addLineForm {
        display: none !important;
    }

    /* Mobile "+ Add line" row — audit doc §3: left-aligned plain text-link, gold,
   no pill/border, its own row with a top divider. */
    .mobile-add-line-card {
        background: #ffffff !important;
        border-left: none;
        box-shadow: none;
        border-top: 1px solid rgba(0, 0, 0, 0.06);
        border-radius: 0;
        margin: 0;
    }

    .mobile-add-line-label {
        padding: 12px 16px;
        color: #b08d57;
        font-weight: 600;
        font-size: 0.85rem;
        font-family: 'Inter', sans-serif;
    }

    .mobile-add-line-label i {
        margin-right: 4px;
    }

    /* Document-pricing banner at the top of the add/edit popups. Deliberately
   muted + labelled "Document pricing": the toggle is document-level state,
   not per-line — flipping it re-interprets every line's entered price. */
    .mobile-pricing-strip {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        padding: 10px 14px;
        background: #eef0f2;
        border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    }

    .mobile-pricing-strip.d-none {
        display: none;
    }

    .mobile-pricing-strip-label {
        font-size: 0.7rem;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        font-weight: 700;
        color: #6c757d;
        white-space: nowrap;
    }

    /* The relocated .v-tax-toggle-wrapper carries its own "Pricing Mode:" label,
   redundant next to the strip's DOCUMENT PRICING label — hide it here only. */
    .mobile-pricing-strip .v-tax-toggle-wrapper>span {
        display: none;
    }
}

/* ----------------------------------------------------------------------
   UNIFIED STICKY PORTAL (Single Compact Strip)
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .mobile-sticky-portal {
        /* PRJ-006: clears the fixed #mobileTopBar (70px, z-index:2200 in modern_sidebar.css).
       Without this the portal sticks at the same y as the top bar and gets hidden behind it
       (top bar has the higher z-index). If this value changes, update the coupled offset on
       .v-sticky-header-bar-wrapper.sticky-top in modern_sidebar.css to match. */
        top: 70px;
        z-index: 2000;
        background: transparent;
    }

    .mobile-doc-strip {
        transition: all 0.3s ease;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        cursor: pointer;
    }

    .mobile-doc-strip:active {
        opacity: 0.85;
    }

    /* PRJ-031 B1: entity-name-primary / cents-superscript / plain-text status treatment */
    .mobile-doc-strip-entity {
        font-weight: 700;
        color: #fff;
        font-size: 0.95rem;
        font-family: 'Merriweather', serif;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        min-width: 0;
    }

    .mobile-doc-strip-money-whole {
        font-family: 'Roboto Mono', monospace;
        font-weight: 700;
        font-size: 1rem;
        color: var(--color-gold);
    }

    .mobile-doc-strip-money-cents {
        /* Size/alignment come from the shared .mobile-money-cents class */
        opacity: 0.85;
        color: var(--color-gold);
    }

    .mobile-doc-strip-status {
        text-transform: lowercase;
        color: rgba(255, 255, 255, 0.55);
        font-size: 0.75rem;
    }
}

/* Luxury Gold Button (State-Aware) */
body.mobile-mode .header-edit-trigger {
    background: #b08d57 !important;
    color: #fff !important;
    border: none !important;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
    transition: all 0.3s;
    box-shadow: 0 4px 15px rgba(176, 141, 87, 0.3);
}

/* Zoom Context Recession */
body.mobile-mode #mobile-orchestrator-root.zoomed-context .mobile-section-card:not(.active-zoomed),
body.mobile-mode #lineItemsContainer.zoomed-context .mobile-line-item-card:not(.active-zoomed) {
    transform: scale(0.92) !important;
    opacity: 0.1 !important;
    filter: blur(8px) !important;
    pointer-events: none;
}

/* PRJ-031 Phase A: a .mobile-section-card that itself wraps independently-zoomable
   children (e.g. a line-items outer card wrapping .mobile-line-card rows) would
   otherwise get recessed by the rule above even while one of its own children is
   the thing being zoomed — CSS filters apply to the whole subtree regardless of
   the child's own computed style. Cancel recession for that case.
   PRJ-031 B3: the line-items container (#lineItemsContainer) is itself a
   .mobile-section-card AND receives the 'zoomed-context' class directly on itself
   (mobileOrchestrator.focusLine() targets the container, not a descendant) — so
   :has(.zoomed-context) alone misses it; without the self-matching clause below,
   the container (and everything under it, via pointer-events inheritance) gets
   recessed and the zoomed row becomes unclickable after the first tap. */
body.mobile-mode #mobile-orchestrator-root.zoomed-context .mobile-section-card:has(.zoomed-context),
body.mobile-mode #mobile-orchestrator-root.zoomed-context .mobile-section-card.zoomed-context {
    transform: none !important;
    opacity: 1 !important;
    filter: none !important;
    pointer-events: auto !important;
}

/* Lower sticky headers when zoomed so the zoomed card can stay on top */
body.mobile-mode #mobile-orchestrator-root.zoomed-context .mobile-sticky-portal {
    z-index: 5 !important;
    opacity: 0.1 !important;
    filter: blur(12px) !important;
    pointer-events: none;
}

/* Unlocks scroll for in-place zoom, but lock-intent is kept for drawer modules */
body.mobile-mode.mobile-interaction-active:not(.drawer-context) {
    overflow: visible !important;
}

@media (max-width: 767.98px) {

    .mobile-section-card,
    .mobile-line-item-card {
        transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        /* PRJ-031 Phase A: mobileOrchestrator.focusHeader()'s scrollIntoView({block:'start'})
       aligns this card's top with viewport y=0, but the fixed #mobileTopBar (70px) plus
       the sticky portal (~80px, see .mobile-sticky-portal above) permanently cover that
       space — without this the card ends up scrolled partly behind them. */
        scroll-margin-top: 150px;
    }

    /* PRJ-031 B2: replaces the Bootstrap `collapse` accordion for the Details/Addresses
   and Message/Terms cards — body is hidden until the card is zoomed. Scoped to this
   media query only, so desktop's `d-md-block` (Bootstrap utility, !important, ≥768px)
   is untouched. */
    .mobile-section-card .mobile-section-body {
        display: none;
    }

    .mobile-section-card.active-zoomed .mobile-section-body {
        display: block;
    }

    .mobile-section-card.active-zoomed .mobile-toggle-icon {
        transform: rotate(90deg);
    }

    /* Bugfix (post-B4): .active-zoomed applies a `transform` to the card
   (scale/translateY below), which breaks `position: sticky` on descendants —
   sticky is computed against the pre-transform layout box but painted through
   the transform, so as the page scrolls the header and the receded background
   behind it fall out of sync and bleed through each other. Once zoomed, the
   header should scroll with the rest of the card instead of staying pinned. */
    .mobile-section-card.active-zoomed .v-sticky-header-bar-wrapper {
        position: static !important;
        top: auto !important;
    }
}

/* Zoom State - Active Card (VICTORY LAYER) */
body.mobile-mode .mobile-section-card.active-zoomed,
body.mobile-mode .mobile-line-item-card.active-zoomed {
    transform: scale(1.05) translateY(-5px) !important;
    box-shadow: 0 40px 100px rgba(0, 0, 0, 0.45) !important;
    z-index: 5000 !important;
    position: relative;
    border-radius: 15px !important;
    border-left: 6px solid #b08d57 !important;
    background: #ffffff !important;
    opacity: 1 !important;
    filter: none !important;
}

body.mobile-mode .active-zoomed .header-edit-fields {
    display: block !important;
}

body.mobile-mode .active-zoomed .header-edit-trigger {
    display: none !important;
}

/* Support scrolling past the bottom when zoomed */
body.mobile-mode #mobile-orchestrator-root.zoomed-context {
    padding-bottom: 400px !important;
}

/* ----------------------------------------------------------------------
   STICKY FOOTER (Main vs Zoom)
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .v-sticky-footer-mobile {
        position: fixed;
        bottom: 0;
        left: 0;
        width: 100%;
        z-index: 1040;
        transition: transform 0.3s ease;
    }
}

body.mobile-mode #mobile-orchestrator-root.zoomed-context~.v-sticky-footer-mobile {
    transform: translateY(100%);
    pointer-events: none;
}

body.mobile-mode .zoom-action-footer {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 6000;
    background: #ffffff;
    border-top: 1px solid rgba(176, 141, 87, 0.2);
    padding: 1rem;
    box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.1);
    display: none;
}

body.mobile-mode.zoom-mode-active .zoom-action-footer {
    display: block !important;
    z-index: 9999 !important;
}

/* PRJ-031 post-B4: line-scoped Save Line/Cancel pair replaces the header's
   FINALISE CHANGES button while a line row is zoomed for editing. Header-zoom
   never carries .line-zoom-mode, so it keeps the single-button layout unchanged. */
.zoom-action-footer .footer-line-actions {
    display: none;
}

body.line-zoom-mode .zoom-action-footer .footer-btn-finalize {
    display: none;
}

body.line-zoom-mode .zoom-action-footer .footer-line-actions {
    display: flex;
}

/* ----------------------------------------------------------------------
   SATISFACTION HAPTICS (Odometer Roll)
   ---------------------------------------------------------------------- */
@keyframes odometerRollUp {
    0% {
        transform: translateY(15px);
        opacity: 0;
    }

    100% {
        transform: translateY(0);
        opacity: 1;
    }
}

body.mobile-mode .odometer-rolling {
    display: inline-block;
    animation: odometerRollUp 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

/* ──────────────────────────────────────────────────────────────────────────
   DASHBOARD — Mobile Layout (PRJ-006)
   ────────────────────────────────────────────────────────────────────────── */

body.mobile-mode .briefing-header {
    margin-top: 0;
}

body.mobile-mode .briefing-header h1 {
    font-size: 1.4rem;
}

/* KPI Cards — 2-column grid; last card spans full width */
body.mobile-mode .metrics-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}

body.mobile-mode .metrics-grid .metric-card:last-child {
    grid-column: 1 / -1;
}

body.mobile-mode .metric-value {
    font-size: 1.4rem;
}

body.mobile-mode .metric-title {
    font-size: 0.72rem;
}

/* Action Centre — 44px min touch target */
body.mobile-mode .btn-approve {
    min-height: 44px;
    padding: 10px 14px;
    white-space: nowrap;
}

body.mobile-mode .drawer-table {
    font-size: 0.78rem;
}

body.mobile-mode .drawer-table td {
    padding: 4px 6px;
}

/* Quick Actions — full-width stacked */
body.mobile-mode .quick-actions-bar .d-flex {
    flex-direction: column;
    align-items: stretch;
}

body.mobile-mode .quick-actions-bar .btn {
    width: 100%;
    padding: 12px;
    font-size: 1rem;
}

/* Charts — stack vertically */
body.mobile-mode .dashboard-split {
    grid-template-columns: 1fr;
    gap: 16px;
}

/* Takealot hover popup — no hover on mobile */
body.mobile-mode .takealot-popup {
    display: none !important;
}

/* ===== Phase 4: List View Card Stack =====
   Scoped to .ledger-container ancestor to avoid hiding takealot/index.html tables
   which use .ledger-table without a .ledger-container ancestor.
   Note: table is nested .ledger-container > .table-responsive > .ledger-table,
   so descendant combinator (not >) is required. */
/* table/card swap already in @media block below — body.mobile-mode kept as progressive enhancement */
body.mobile-mode .ledger-container .ledger-table {
    display: none !important;
}

body.mobile-mode .mobile-list-card {
    display: flex !important;
}

.mobile-list-card {
    display: none;
    align-items: stretch;
    background: #fff;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    cursor: pointer;
    transition: background 0.15s;
    text-decoration: none;
    color: inherit;
}

.mobile-list-card:active {
    background: rgba(212, 176, 104, 0.08);
}

.mobile-card-accent {
    width: 4px;
    flex-shrink: 0;
}

.mobile-card-body {
    flex: 1;
    padding: 12px 14px;
    min-width: 0;
}

.mobile-card-docno {
    font-size: 0.85rem;
    color: #2C3539;
    white-space: nowrap;
}

.mobile-card-entity {
    font-size: 0.78rem;
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* PRJ-026 Phase 8.8: name-first hierarchy for ledger_row()'s mobile card
   (invoices/quotes/bills/orders/returns). Separate classes from
   .mobile-card-docno/.mobile-card-entity above — those stay as-is for
   linear_item_row() (Customers/Suppliers/Inventory/etc.), which keeps the
   original doc-number-first hierarchy per user decision (2026-08-15). */
.mobile-card-entity-primary {
    font-size: 0.85rem;
    color: #2C3539;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mobile-card-docno-secondary {
    font-size: 0.78rem;
    color: #6C757D;
    margin-top: 2px;
    white-space: nowrap;
}

.mobile-card-amount {
    font-family: 'Roboto Mono', monospace;
    font-size: 0.9rem;
    color: #2C3539;
    white-space: nowrap;
}

/* ===== PRJ-024 Phase 1: Cashbook Mobile ===== */

/* Hide desktop table on mobile; cashbook cards take over */
body.mobile-mode #cashbookTable {
    display: none !important;
}

body.mobile-mode .mobile-cashbook-card {
    display: flex !important;
}

/* ── Cashbook Card ── */
.mobile-cashbook-card {
    display: none;
    position: relative;
    background: #fff;
    border-bottom: 1px solid rgba(0, 0, 0, 0.07);
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    transition: background 0.12s;
    overflow: hidden;
}

.mobile-cashbook-card:active {
    background: rgba(212, 175, 55, 0.07);
}

.mcb-accent {
    width: 4px;
    flex-shrink: 0;
    background: var(--color-gold, #D4B068);
}

.mcb-accent.reconciled {
    background: #10B981;
}

.mcb-accent.unallocated {
    background: #ccc;
}

.mcb-body {
    flex: 1;
    padding: 12px 14px;
    min-width: 0;
}

.mcb-row1 {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin-bottom: 2px;
}

.mcb-type {
    font-size: 0.78rem;
    font-weight: 600;
    color: #2C3539;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
}

.mcb-date {
    font-size: 0.75rem;
    color: #6c757d;
    white-space: nowrap;
    flex-shrink: 0;
}

.mcb-entity {
    font-size: 0.85rem;
    font-weight: 500;
    color: #2C3539;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-bottom: 1px;
}

.mcb-desc {
    font-size: 0.75rem;
    color: #6c757d;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mcb-row3 {
    display: flex;
    justify-content: flex-end;
    margin-top: 4px;
}

.mcb-amount {
    font-family: 'Roboto Mono', monospace;
    font-size: 0.95rem;
    font-weight: 700;
}

/* Status icon area (lock / pencil / duplicate) */
.mcb-status-icons {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 0 10px 0 6px;
    gap: 4px;
    color: #6c757d;
    font-size: 0.85rem;
    flex-shrink: 0;
}

.mcb-status-icons .reconciled-icon {
    color: #10B981;
}

.mcb-status-icons .dup-icon {
    color: var(--color-gold, #D4B068);
}

/* ── Selection Mode ── */
.mobile-selection-mode .mobile-cashbook-card {
    cursor: default;
}

.mobile-card-checkbox {
    display: none;
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    cursor: pointer;
    z-index: 2;
    accent-color: var(--color-gold, #D4B068);
}

.mobile-selection-mode .mobile-card-checkbox {
    display: block;
}

.mobile-selection-mode .mcb-accent {
    opacity: 0;
}

.mobile-selection-mode .mcb-body {
    padding-left: 42px;
}

#mobileSelectionToolbar {
    display: none;
    position: fixed;
    bottom: 70px;
    left: 0;
    right: 0;
    z-index: 1060;
    background: #2C3539;
    color: #fff;
    padding: 10px 16px;
    gap: 10px;
    align-items: center;
    justify-content: space-between;
}

#mobileSelectionToolbar.visible {
    display: flex;
}

/* ── Sticky Context Bar ── */
#mobileCashbookBar {
    position: sticky;
    top: 0;
    z-index: 1020;
    background: #2C3539;
    color: #fff;
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    transition: padding 0.2s, font-size 0.2s;
    border-bottom: 2px solid var(--color-gold, #D4B068);
}

#mobileCashbookBar.collapsed {
    padding: 8px 16px;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
}

#mobileCashbookBar .bar-account-name {
    font-size: 1rem;
    font-weight: 700;
    color: var(--color-gold, #D4B068);
}

#mobileCashbookBar.collapsed .bar-account-name {
    font-size: 0.85rem;
}

#mobileCashbookBar .bar-subtitle {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.7);
}

#mobileCashbookBar.collapsed .bar-subtitle {
    display: none;
}

#mobileCashbookBar .bar-total {
    font-family: 'Roboto Mono', monospace;
    font-size: 0.9rem;
    color: #fff;
}

/* ── Bottom Sheet ── */
.mobile-sheet-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 1070;
}

.mobile-sheet-backdrop.visible {
    display: block;
}

.mobile-bottom-sheet {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: #fff;
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.18);
    z-index: 1080;
    max-height: 92vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    transform: translateY(100%);
    transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}

.mobile-bottom-sheet.open {
    transform: translateY(0);
}

.mobile-bottom-sheet.stacked {
    z-index: 1090;
    max-height: 88vh;
}

.mobile-sheet-handle {
    width: 36px;
    height: 4px;
    background: #ddd;
    border-radius: 2px;
    margin: 12px auto 0;
}

.mobile-sheet-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.mobile-sheet-title {
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #2C3539;
}

.mobile-sheet-body {
    padding: 16px;
}

.mobile-sheet-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #6c757d;
    margin-bottom: 4px;
}

.mobile-sheet-input {
    width: 100%;
    padding: 12px;
    font-size: 1rem;
    border: 1px solid #dee2e6;
    border-radius: 8px;
    background: #fafafa;
    margin-bottom: 12px;
    color: #2C3539;
    -webkit-appearance: none;
}

.mobile-sheet-input:focus {
    outline: none;
    border-color: var(--color-gold, #D4B068);
    box-shadow: 0 0 0 2px rgba(212, 175, 55, 0.15);
    background: #fff;
}

.mobile-sheet-input:disabled {
    background: #f5f5f5;
    color: #aaa;
}

.mobile-amount-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-bottom: 12px;
}

.mobile-drawer-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    padding: 14px;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    border: none;
    cursor: pointer;
    margin-bottom: 10px;
}

.mobile-drawer-btn-match {
    background: rgba(212, 175, 55, 0.1);
    color: #2C3539;
    border: 1px solid rgba(212, 175, 55, 0.4);
}

.mobile-drawer-btn-delete {
    background: rgba(166, 44, 43, 0.08);
    color: #A62C2B;
    border: 1px solid rgba(166, 44, 43, 0.2);
}

.mobile-drawer-btn-save {
    background: var(--color-gold, #D4B068);
    color: #2C3539;
    font-size: 1rem;
    font-weight: 700;
    padding: 16px;
    margin-top: 6px;
    margin-bottom: 0;
}

.mobile-drawer-btn-save:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* ── Sticky Footer (Post & Reconcile) ── */
#mobileCashbookFooter {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1050;
    padding: 10px 16px;
    background: #fff;
    border-top: 1px solid rgba(0, 0, 0, 0.1);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
}

/* Reconcile bar: the button plus an (i) that explains how to select (touch and hold). */
#mobileCashbookFooter .cb-footer-row { display: flex; align-items: center; gap: 10px; }
#mobileCashbookFooter .cb-footer-i {
    width: 48px; height: 48px; flex: 0 0 auto; border-radius: 50%;
    border: 1px solid var(--color-champagne, #E9D6B1); background: transparent; color: var(--color-charcoal, #2C3539);
    font-size: 1.15rem; line-height: 1; padding: 0;
}
#mobileCashbookFooter .cb-footer-i:focus-visible { outline: 2px solid var(--color-charcoal, #2C3539); outline-offset: 2px; }
#mobileCashbookFooter .cb-footer-hint {
    /* right: 84px keeps it clear of the + button (52px + 16px edge + gap) which sits above this bar */
    position: absolute; left: 16px; right: 84px; bottom: calc(100% + 8px);
    padding: 10px 12px; border-radius: 10px; font-size: .9rem;
    background: var(--color-charcoal, #2C3539); color: #fff; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

/* Hide desktop cashbook chrome on mobile — wrapped in .cashbook-desktop-chrome by the template */
body.mobile-mode .cashbook-desktop-chrome {
    display: none !important;
}

/* Hide the list.header() page title on the cashbook page — context bar replaces it.
   Scoped to #cashbookHeaderTop only: the old bare .v-crm-header-top selector had
   higher specificity than the P10 "compact header" media query below and won
   regardless of source order, silently hiding the header (and every button in
   it — take-on nav, Confirm Reviewed, New) on every mobile list page, not just
   cashbook. */
body.mobile-mode #cashbookHeaderTop {
    display: none !important;
}

/* ── Mobile Filter Panel ── */
#mobileCashbookFilters {
    background: #2C3539;
    padding: 0 16px;
    overflow: hidden;
    max-height: 0;
    transition: max-height 0.3s ease, padding 0.3s ease;
}

#mobileCashbookFilters.open {
    max-height: 300px;
    padding: 12px 16px 16px;
}

.mobile-filter-label {
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: rgba(255, 255, 255, 0.55);
    margin-bottom: 4px;
}

.mobile-filter-select {
    width: 100%;
    padding: 10px 12px;
    font-size: 0.9rem;
    border: 1px solid rgba(212, 175, 55, 0.35);
    border-radius: 8px;
    background: #1e2a2f;
    color: #fff;
    margin-bottom: 10px;
    -webkit-appearance: none;
}

.mobile-filter-select option {
    background: #1e2a2f;
    color: #fff;
}

.mobile-filter-view-toggle {
    display: flex;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid rgba(212, 175, 55, 0.35);
    margin-bottom: 12px;
}

.mobile-filter-view-btn {
    flex: 1;
    padding: 10px;
    background: transparent;
    color: rgba(255, 255, 255, 0.6);
    border: none;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
}

.mobile-filter-view-btn.active {
    background: var(--color-gold, #D4B068);
    color: #2C3539;
}

/* ── FAB (Add Transaction) ── */
#mobileCashbookFab {
    position: fixed;
    bottom: 80px;
    right: 16px;
    z-index: 1055;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: var(--color-gold, #D4B068);
    color: #2C3539;
    border: none;
    font-size: 1.4rem;
    font-weight: 700;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.15s, box-shadow 0.15s;
}

#mobileCashbookFab:active {
    transform: scale(0.92);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

/* Shift FAB up when selection toolbar is visible */
#mobileSelectionToolbar.visible~* #mobileCashbookFab {
    bottom: 140px;
}

/* The feedback bubble must sit ABOVE this FAB, not on it: the generic list rule puts it at 88px, which is
   exactly where this FAB (80px + 52px) is on the Cash Book. 80 + 52 + 12 = 144px; 140 + 52 + 12 = 204px when
   the selection toolbar has lifted the FAB. !important for the same reason as the generic rule. */
@media (max-width: 767.98px) {
    body:has(#mobileCashbookFab) .v-feedback-trigger {
        bottom: 144px !important;
    }
    body:has(#mobileSelectionToolbar.visible) .v-feedback-trigger {
        bottom: 204px !important;
    }
}

/* ── Mobile Empty State ── */
.mobile-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    text-align: center;
    color: rgba(255, 255, 255, 0.45);
}

.mobile-empty-icon {
    font-size: 2.8rem;
    margin-bottom: 12px;
    color: var(--color-gold, #D4B068);
    opacity: 0.5;
}

.mobile-empty-title {
    font-size: 1rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.7);
    margin-bottom: 6px;
}

.mobile-empty-hint {
    font-size: 0.82rem;
    color: rgba(255, 255, 255, 0.4);
    line-height: 1.5;
}

@media (max-width: 767.98px) {
    .ledger-card {
        padding: 0 !important;
        box-shadow: none !important;
        border-radius: 0 !important;
        background: transparent !important;
    }

    .ledger-card .table-responsive {
        max-height: none !important;
    }
}

.mobile-card-status .v-badge-glass {
    font-size: 0.55rem;
}

/* ===== PRJ-025: Mobile Action Sheet =====
   Bottom sheet for document workflow actions (Post, PDF, Email, etc.)
   Reuses .mobile-sheet-handle from cashbook bottom sheet pattern. */

.mobile-action-sheet-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 1070;
}

.mobile-action-sheet-backdrop.visible {
    display: block;
}

.mobile-action-sheet {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: #fff;
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.18);
    z-index: 1080;
    max-height: 85vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    transform: translateY(100%);
    transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
    padding-bottom: env(safe-area-inset-bottom, 0);
}

.mobile-action-sheet.open {
    transform: translateY(0);
}

.mobile-action-sheet-list {
    padding: 8px 0;
}

.mobile-action-sheet-row {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: 16px 20px;
    background: none;
    border: none;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
    font-size: 1rem;
    color: #2C3539;
    cursor: pointer;
    text-align: left;
    min-height: 52px;
}

.mobile-action-sheet-row:active {
    background: rgba(212, 175, 55, 0.08);
}

.mobile-action-sheet-row:last-child {
    border-bottom: none;
}

.mobile-action-sheet-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    background: rgba(44, 53, 57, 0.06);
    color: #2C3539;
    font-size: 1.1rem;
    flex-shrink: 0;
}

.mobile-action-sheet-text {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.mobile-action-sheet-label {
    font-weight: 600;
}

.mobile-action-sheet-desc {
    font-size: 0.78rem;
    color: #6c757d;
    line-height: 1.3;
    margin-top: 2px;
}

.mobile-action-sheet-separator {
    height: 1px;
    background: rgba(166, 44, 43, 0.15);
    margin: 4px 20px;
}

.mobile-action-sheet-danger .mobile-action-sheet-icon {
    background: rgba(166, 44, 43, 0.08);
    color: #A62C2B;
}

.mobile-action-sheet-danger .mobile-action-sheet-label {
    color: #A62C2B;
}

.mobile-action-sheet-cancel {
    padding: 8px 16px 16px;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
}

.mobile-action-sheet-cancel-btn {
    display: block;
    width: 100%;
    padding: 14px;
    background: rgba(44, 53, 57, 0.06);
    border: none;
    border-radius: 12px;
    font-size: 1rem;
    font-weight: 600;
    color: #2C3539;
    cursor: pointer;
    min-height: 48px;
}

.mobile-action-sheet-cancel-btn:active {
    background: rgba(44, 53, 57, 0.12);
}

/* ── Mobile Action Bar: floating pill row (PRJ-031 B4) ──
   Ported from templates/dev/mobile_card_stack_mockup.html's .csm-action-bar.
   Post/dominant action (btn-viljoen-gold) fills most of the row; the
   secondary action (btn-platinum) is a small outlined icon+label pill;
   "more" is a small circular icon button. All 7 document types' mobile
   action bars use exactly this two-button shape (one gold, one platinum),
   confirmed against every *_edit.html route file, so this is a CSS-only
   port — no macro/template changes needed. */
@media (max-width: 767.98px) {
    .mobile-action-bar {
        position: fixed;
        left: 16px;
        right: 16px;
        bottom: calc(16px + env(safe-area-inset-bottom, 0));
        z-index: 1040;
        background: #fff;
        border-top: none;
        border-radius: 30px;
        padding: 8px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .mobile-action-bar .mobile-action-primary {
        min-height: 44px;
        font-size: 0.85rem;
        font-weight: 600;
        border-radius: 24px;
        border: none;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
    }

    /* Dominant action (Post/PDF/Convert — whichever route marks it gold) */
    .mobile-action-bar .mobile-action-primary.btn-viljoen-gold {
        flex-grow: 1;
        padding: 12px 16px;
        font-weight: 700;
        font-size: 0.9rem;
    }

    /* Secondary action (Save/Email/PDF — whichever route marks it platinum) */
    .mobile-action-bar .mobile-action-primary.btn-platinum {
        flex-grow: 0;
        flex-shrink: 0;
        padding: 10px 16px;
        border: 1px solid #dee2e6 !important;
        background: #fff !important;
        color: #2C3539 !important;
    }

    .mobile-action-bar .mobile-action-more {
        width: 44px;
        height: 44px;
        border-radius: 50%;
        background: #fff;
        border: 1px solid #dee2e6;
        color: #2C3539;
        font-size: 1.1rem;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

    .mobile-action-bar .mobile-action-more:active {
        background: rgba(44, 53, 57, 0.06);
    }
}

/* Hide action bar when zoom mode is active — FINALIZE footer takes over */
body.mobile-mode.zoom-mode-active .mobile-action-bar {
    display: none;
}

/* ===== PRJ-025 Phase 3: Mobile Selection Mode =====
   Long-press to select, tap to toggle, bottom toolbar for bulk actions. */

.mobile-select-checkbox {
    display: none;
    position: absolute;
    top: 8px;
    left: 8px;
    width: 22px;
    height: 22px;
    z-index: 2;
    accent-color: var(--color-gold, #D4B068);
}

.mobile-selection-mode .mobile-select-checkbox {
    display: block;
}

.mobile-selection-mode .mobile-list-card {
    padding-left: 36px !important;
    position: relative;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}

.mobile-card-selected {
    background: rgba(212, 175, 55, 0.08) !important;
    border-left: 4px solid var(--color-gold, #D4B068) !important;
}

/* Selection toolbar — fixed bottom bar */
.mobile-selection-toolbar {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1060;
    background: #2C3539;
    color: #fff;
    padding: 12px 16px;
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0));
    align-items: center;
    justify-content: space-between;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.25);
}

.mobile-selection-toolbar.visible {
    display: flex;
}

.mobile-selection-toolbar .mobile-selection-count {
    font-size: 0.85rem;
    font-weight: 600;
}

.mobile-selection-toolbar .mobile-selection-actions {
    display: flex;
    gap: 8px;
    align-items: center;
}

.mobile-selection-toolbar .mobile-selection-btn {
    padding: 8px 14px;
    border-radius: 6px;
    border: none;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    min-height: 40px;
}

.mobile-selection-toolbar .mobile-selection-btn-primary {
    background: var(--color-gold, #D4B068);
    color: #2C3539;
}

.mobile-selection-toolbar .mobile-selection-btn-danger {
    background: rgba(166, 44, 43, 0.15);
    color: #ff6b6b;
}

.mobile-selection-toolbar .mobile-selection-btn-cancel {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
}

/* FAB — Create button for list pages */
.mobile-list-fab {
    display: none;
    position: fixed;
    bottom: 24px;
    right: 16px;
    z-index: 1055;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--color-gold, #D4B068);
    color: #2C3539;
    border: none;
    font-size: 1.5rem;
    font-weight: 700;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
    cursor: pointer;
    align-items: center;
    justify-content: center;
    transition: transform 0.15s, box-shadow 0.15s;
}

@media (max-width: 767.98px) {
    .mobile-list-fab {
        display: flex;
    }

    /* .v-feedback-trigger (feedback_widget.html) sits at bottom-right on every
       page, same 24px bottom offset as the list FAB — on mobile list pages the
       two circles stack directly on top of each other (Report a Bug hidden
       behind New). Stack feedback directly above the FAB instead (same right
       offset as the FAB, cleared by the FAB's height + a gap) so both stay
       reachable with the thumb without competing for the same spot.
       !important: feedback_widget.html's own <style> block defines
       .v-feedback-trigger unscoped (no media query) and loads after this
       stylesheet, so at equal specificity it wins the cascade regardless of
       viewport — plain overrides here are silently ignored on mobile. */
    .v-feedback-trigger {
        bottom: 88px !important;
        right: 16px !important;
    }
}

.mobile-list-fab:active {
    transform: scale(0.92);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

/* Shift FAB up when selection toolbar visible */
.mobile-selection-toolbar.visible~.mobile-list-fab {
    bottom: 80px;
}

/* ===== PRJ-025 P9: Recent Picks Strip ===== */
.recent-picks-strip {
    margin-bottom: 8px;
    padding: 0 2px;
}

.recent-picks-label {
    display: block;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #6c757d;
    margin-bottom: 4px;
}

.recent-picks-chips {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: 6px;
}

.recent-pick-chip {
    display: inline-flex;
    align-items: center;
    padding: 8px 12px;
    border-radius: 16px;
    border: 1px solid var(--color-gold, #D4B068);
    background: rgba(212, 176, 104, 0.08);
    color: #2C3539;
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    min-height: 44px;
    transition: background 0.15s;
}

.recent-pick-chip:active {
    background: rgba(212, 176, 104, 0.2);
}

/* ===== PRJ-025: Mobile Section Collapse Toggle =====
   Chevron rotation + compact section headers for collapsible detail areas. */
@media (max-width: 767.98px) {
    .mobile-section-toggle {
        font-size: 0.85rem;
        font-weight: 600;
        letter-spacing: 0.5px;
        color: #2C3539;
        text-transform: uppercase;
    }

    .mobile-toggle-icon {
        transition: transform 0.2s ease;
        display: inline-block;
    }

    .mobile-section-toggle[aria-expanded="true"] .mobile-toggle-icon {
        transform: rotate(90deg);
    }
}

/* ===== PRJ-025 P10: Compact List Page Headers on Mobile =====
   Hide the desktop header block (title + bulk actions) — context is clear from nav.
   Create button moves to FAB or stays as a smaller element. */
@media (max-width: 767.98px) {
    .v-crm-header-top {
        padding: 8px 0 4px !important;
        flex-wrap: wrap;
        gap: 8px;
    }

    .v-crm-header-top .v-crm-title {
        font-size: 1.1rem !important;
        margin-bottom: 0;
    }

    .v-crm-header-top .v-actions-group {
        flex-wrap: wrap;
        gap: 6px;
    }

    .v-crm-header-top .v-header-extra {
        font-size: 0.78rem;
    }

    .v-crm-header-top .v-btn-crm {
        font-size: 0.78rem;
        padding: 6px 12px;
    }
}

/* ===== PRJ-025 P11: Scrollable Filter/Status Strips =====
   Prevent horizontal overflow on status toggle groups and search composites. */
@media (max-width: 767.98px) {
    .status-toggle-group {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        flex-shrink: 0;
        margin-left: 0 !important;
        border-left: none !important;
        padding-bottom: 2px;
        max-width: 100vw;
        flex-wrap: nowrap;
    }

    .ledger-search-bar,
    .v-crm-search-composite {
        flex-wrap: wrap;
    }

    .ledger-search-bar .search-input-group {
        flex: 1 1 100%;
        margin-bottom: 6px;
    }

    .ledger-search-bar .status-toggle-group {
        flex: 1 1 100%;
        justify-content: flex-start;
        gap: 6px;
    }

    /* PRJ-026 Phase 8.1: pill-shaped filter chips (style guide §9.5) — shape/colour
       only, reuses the scroll behaviour already set on .status-toggle-group above.
       Selector doubled (.status-toggle-group .status-btn) for specificity: the
       macro's own unconditional (non-media-queried) .status-btn rule in
       _ledger_macros.html's inline <style> block renders later in <head> source
       order than this external stylesheet, so a same-specificity bare .status-btn
       rule here would lose the cascade at every viewport width, not just desktop. */
    .status-toggle-group .status-btn {
        font-size: 0.74rem;
        padding: 8px 18px;
        white-space: nowrap;
        flex-shrink: 0;
        min-height: 34px;
        border-radius: 999px;
        border: 1px solid var(--v-border, #C4C4C4);
        color: var(--v-text-grey, #6C757D);
    }

    .status-toggle-group .status-btn.active {
        border-color: var(--v-charcoal, #2C3539);
    }
}

/* ===== PRJ-025 P12: Prevent table text overflow on mobile =====
   Fallback for any table that isn't replaced by cards. */
@media (max-width: 767.98px) {

    .table th,
    .table td {
        font-size: 0.75rem;
        padding: 4px 3px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        max-width: 80px;
    }
}

/* ===== PRJ-025: CSS-only table/card swap fallback =====
   Media query backup — doesn't depend on body.mobile-mode JS loading.
   Ensures cards show and table hides even before MobileOrchestrator runs. */
@media (max-width: 767.98px) {
    .ledger-container .ledger-table {
        display: none !important;
    }

    .mobile-list-card {
        display: flex !important;
    }
}

/* ===== PRJ-025: Global mobile overflow prevention =====
   Ensure nothing bleeds past the viewport on mobile. */
@media (max-width: 767.98px) {
    main {
        /* PRJ-006: overflow-x:hidden forces overflow-y:auto per spec, which breaks
       position:sticky for every descendant. overflow-x:clip clips the same way
       without that side-effect. See modern_sidebar.css:69 for prior art. */
        overflow-x: clip;
        max-width: 100vw;
    }

    /* PRJ-026 Phase 8.7: .v-crm-sticky-top defaults to top:0/z-index:1020
       (list_views.css), which sticks it BEHIND the fixed #mobileTopBar (70px,
       z-index:2200, modern_sidebar.css) — the same clearance problem already
       documented and solved for .mobile-sticky-portal above. Only became visible
       once mobile_context_heading() moved inside this wrapper (8.7); the search
       bar alone didn't show the overlap as clearly. */
    .v-crm-sticky-top {
        max-width: 100vw;
        overflow-x: hidden;
        top: 70px;
        z-index: 1098;
    }

    .ledger-container {
        max-width: 100vw;
        overflow-x: hidden;
        padding: 0 8px;
    }

    /* Pagination: compact on mobile */
    .v-pagination {
        flex-wrap: wrap;
        gap: 4px;
        justify-content: center;
    }

    .v-pagination .page-link {
        padding: 6px 10px;
        font-size: 0.75rem;
    }

    /* Per-page selector: compact. .v-pagination-size (list_view.html's
       pagination_size_selector macro, used by Chart of Accounts and other
       v-crm-container list pages) is a distinct class from the ledger-page
       equivalents below — without it listed here it fell through uncompacted
       on mobile, crowding the Previous/page-numbers/per-page row into an
       overflowing mess at the bottom of the list. */
    .v-per-page,
    .ledger-per-page,
    .v-pagination-size {
        font-size: 0.75rem;
    }

    .v-per-page select,
    .ledger-per-page select,
    .v-pagination-size select {
        padding: 4px 6px;
        font-size: 0.75rem;
    }

    /* Pagination row (list_view.html's pagination_control +
       pagination_size_selector, side-by-side via justify-content-between on
       desktop): wrap and center on mobile instead of overflowing, and clear
       the fixed mobile-list-fab (56px + 24px bottom offset) sitting on top
       of it. */
    .v-crm-pagination-row {
        flex-wrap: wrap;
        justify-content: center !important;
        gap: 12px;
        padding-bottom: 96px !important;
    }

    .v-crm-pagination-row .v-pagination-size {
        margin: 0 auto !important;
    }

    /* PRJ-026 Phase 8.6: Refine collapses to an icon inside the search field on
       mobile instead of wrapping to its own full-width row below the chips.
       .ledger-refine-btn-mobile is a SEPARATE button rendered inside
       .search-input-group in the macro markup (not the desktop .ledger-refine-btn
       repositioned) — sibling-absolute-positioning was tried first and rejected
       because .search-unit-container can also render .ledger-title-unit
       (takealot/staging.html passes title= into ledger_search()), which breaks any
       CSS assuming the search bar is always the first wrapped row. Two buttons,
       one drawer: both carry data-refine-toggle, so the existing global
       click-delegation handler in ledger_scripts() needs no change. The desktop
       .ledger-refine-btn is d-none d-md-flex; this one is d-md-none — exactly one
       is visible at any viewport width. */
    .search-input-group {
        position: relative;
    }

    .search-unit-container .search-bar-wrapper .search-input-group input {
        padding-right: 36px;
    }

    .ledger-refine-btn-mobile {
        position: absolute;
        top: 50%;
        right: 4px;
        transform: translateY(-50%);
        z-index: 2;
        background: transparent !important;
        border: none !important;
        padding: 6px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .ledger-refine-btn-mobile i {
        font-size: 1.05rem;
        color: var(--v-text-grey, #6C757D);
    }

    .ledger-refine-btn-mobile.active i {
        color: var(--v-charcoal, #2C3539);
    }

    /* Search bar and filter area — stack vertically on mobile */
    .search-unit-container {
        flex-wrap: wrap !important;
        gap: 8px !important;
        padding: 10px 8px !important;
    }

    .search-unit-container .ledger-title-unit {
        flex: 1 1 100%;
        margin-right: 0 !important;
    }

    /* PRJ-026 Phase 8.6: widen the search+chips box to use the full row now that
       the desktop Refine button no longer sits beside it taking up space. */
    .search-unit-container .search-bar-wrapper {
        flex: 1 1 100%;
        width: 100%;
        min-width: 0;
        flex-wrap: wrap !important;
    }

    .search-unit-container .search-bar-wrapper .search-input-group {
        flex: 1 1 100%;
        margin-bottom: 8px;
    }
}

@media (max-width: 767.98px) {
    .search-bar-wrapper {
        flex-wrap: wrap !important;
    }

    .search-bar-wrapper .search-input-group {
        flex: 1 1 100% !important;
        margin-bottom: 6px;
    }

    .search-bar-wrapper .status-toggle-group {
        flex: 1 1 100% !important;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 4px;
    }
}

@media (max-width: 767.98px) {
    .search-unit-container .ledger-refine-btn {
        margin-left: 0 !important;
        flex-shrink: 0;
    }

    .ledger-control-row {
        max-width: 100%;
        overflow: hidden;
    }

    /* Refine drawer — full-width single-column on mobile */
    .refine-drawer {
        max-width: 100%;
        overflow-x: hidden;
        padding: 12px 8px;
    }

    .refine-drawer .refine-content {
        display: flex !important;
        flex-direction: column !important;
        gap: 10px;
    }

    .refine-drawer .grid-layout {
        grid-template-columns: 1fr !important;
    }

    .refine-drawer .refine-field {
        width: 100%;
    }

    .refine-drawer .refine-field label {
        font-size: 0.75rem;
    }

    .refine-drawer input,
    .refine-drawer select {
        width: 100%;
        font-size: 0.85rem;
    }

    .refine-drawer .apply-btn {
        width: 100%;
        min-height: 44px;
    }
}

/* Prevent ANY horizontal scroll on mobile */
@media (max-width: 767.98px) {

    html,
    body {
        /* PRJ-006: overflow-x:hidden forces overflow-y:auto per spec, breaking
           position:sticky app-wide on mobile. overflow-x:clip avoids that side-effect. */
        overflow-x: clip;
        max-width: 100vw;
    }
}

/* ===== PRJ-025 P13: Force sidebar closed on mobile =====
   CSS safety net — even if JS hasn't run yet, sidebar stays hidden. */
body.mobile-mode #sidebarMenu.pinned {
    transform: translateX(-100%) !important;
}

body.mobile-mode #sidebarMenu.pinned.show {
    transform: translateX(0) !important;
}

/* Portal must sit BELOW sidebar (sidebar z:2000) so menu covers it */
body.mobile-mode .mobile-sticky-portal {
    z-index: 1050 !important;
}

/* When sidebar is open (.show), hide the portal entirely to prevent bleed-through */
body.mobile-mode #sidebarMenu.show~main .mobile-sticky-portal,
body.mobile-mode #sidebarMenu.show~.mobile-sticky-portal {
    display: none !important;
}

/* ===== PRJ-023: Mobile Navigation — Full-Screen Accordion Drawer =====
   All rules gated on body.mobile-mode — zero desktop impact.
   Desktop hover-flyout (main.css .sidebar .v-submenu) is completely untouched.
   The click-accordion is powered by the existing .submenu-open toggle in main.js. */

/* Sidebar fills full viewport width on mobile */
body.mobile-mode #sidebarMenu {
    width: 100vw !important;
    overflow-y: auto;
    overflow-x: hidden;
}

/* 48px minimum touch targets on all nav links */
body.mobile-mode #sidebarMenu .nav-link {
    min-height: 48px !important;
    display: flex !important;
    align-items: center !important;
}

/* Convert the fixed-position flyout to a static inline block.
   positionSubmenu() in main.js sets inline top/left — they are ignored on position:static. */
body.mobile-mode .sidebar .v-submenu {
    position: static !important;
    left: auto !important;
    top: auto !important;
    display: none !important;
    width: 100% !important;
    min-width: 0 !important;
    max-height: none !important;
    box-shadow: none !important;
    border-radius: 0 !important;
    border: none !important;
    border-left: 3px solid rgba(212, 175, 55, 0.4) !important;
    margin-left: 12px !important;
    animation: none !important;
    background: rgba(255, 255, 255, 0.04) !important;
}

/* Disable the CSS hover trigger (touch "hover" is sticky on iOS/Android) */
body.mobile-mode .sidebar .nav-item:hover>.v-submenu {
    display: none !important;
}

/* Show accordion panel via the existing .submenu-open class (toggled by main.js click) */
body.mobile-mode .sidebar .nav-item.submenu-open>.v-submenu {
    display: block !important;
}

/* Rotate chevron to point down when section is expanded */
body.mobile-mode .sidebar .nav-item.submenu-open>.v-has-submenu::after {
    content: "\F282" !important;
}

/* ======================================================================
   PRJ-025 Phase 7A: Mobile List Page — KPI Bar + Avatars + Card Enhancements
   Scoped to @media queries per P17 (CSS-First). Desktop unchanged.
   ====================================================================== */

/* ── P19: Contextual Page Heading ── */
.mobile-context-heading {
    display: none;
}

@media (max-width: 767.98px) {
    .mobile-context-heading {
        display: block;
        padding: 16px 16px 8px;
        background: var(--color-charcoal, #2C3539);
        border-bottom: 2px solid var(--color-gold, #D4B068);
    }

    .mobile-context-title {
        font-family: 'Merriweather', serif;
        font-size: 1.3rem;
        font-weight: 700;
        color: var(--color-pure-white, #fff);
        margin: 0;
    }

    .mobile-context-subtitle {
        font-size: 0.75rem;
        color: rgba(255, 255, 255, 0.55);
        margin: 2px 0 0;
    }

    /* Pages with .mobile-context-heading hide the desktop header via per-page styles (P14) */
}

/* ── KPI Summary Bar ── */
.mobile-kpi-bar {
    display: none;
}

@media (max-width: 767.98px) {
    .mobile-kpi-bar {
        display: flex;
        gap: 8px;
        padding: 12px 12px 8px;
        background: var(--color-off-white, #F8F9FA);
        border-bottom: 1px solid rgba(44, 53, 57, 0.08);
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .mobile-kpi-card {
        flex: 1;
        min-width: 0;
        background: var(--color-pure-white, #fff);
        border-radius: 10px;
        padding: 10px 12px;
        text-align: center;
        border: 1px solid rgba(212, 176, 104, 0.15);
        box-shadow: 0 1px 3px rgba(44, 53, 57, 0.06);
    }

    .mobile-kpi-value {
        font-family: 'Roboto Mono', monospace;
        font-size: 0.95rem;
        font-weight: 700;
        color: var(--color-charcoal, #2C3539);
        white-space: nowrap;
    }

    .mobile-kpi-value.kpi-danger {
        color: var(--color-crimson, #A62C2B);
    }

    .mobile-kpi-value.kpi-success {
        color: var(--v-banker-green, #1B4D3E);
    }

    .mobile-kpi-label {
        font-size: 0.6rem;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        color: var(--color-text-grey, #6C757D);
        margin-top: 2px;
    }

    .mobile-kpi-count {
        font-size: 0.6rem;
        color: var(--color-text-grey, #6C757D);
        opacity: 0.6;
    }

    /* Extra padding at bottom for FAB / selection toolbar */
    .ledger-container {
        padding-bottom: 80px;
    }
}

/* ── Customer Initials Avatar ── */
.mobile-card-avatar {
    display: none;
}

@media (max-width: 767.98px) {
    .mobile-card-avatar {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 38px;
        height: 38px;
        min-width: 38px;
        border-radius: 50%;
        font-size: 0.72rem;
        font-weight: 700;
        color: var(--color-pure-white, #fff);
        letter-spacing: 0.03em;
        margin: auto 0;
        margin-left: 10px;
    }

    .mobile-card-body {
        padding: 12px 14px 12px 10px;
    }

    .mobile-card-amount {
        color: var(--color-charcoal, #2C3539);
        font-size: 0.92rem;
    }
}

/* ── Relative Date + Overdue ── */
.mobile-card-reldate {
    font-size: 0.7rem;
    color: var(--color-text-grey, #6C757D);
}

.mobile-card-reldate.mobile-card-overdue {
    color: var(--color-crimson, #A62C2B);
    font-weight: 600;
}

.mobile-card-date-sub {
    font-size: 0.68rem;
    color: var(--color-text-grey, #6C757D);
    opacity: 0.5;
}

/* ── Status pills (mobile) ── */
@media (max-width: 767.98px) {
    .mobile-card-status .v-badge-glass {
        font-size: 0.58rem;
        padding: 2px 8px;
        border-radius: 10px;
        letter-spacing: 0.04em;
    }
}

/* ======================================================================
   PRJ-026 P18: Swipe Actions — reveal action tray behind card
   ====================================================================== */
@media (max-width: 767.98px) {
    .mobile-list-card {
        position: relative;
        overflow: hidden;
    }

    .mobile-card-inner {
        display: flex;
        align-items: stretch;
        background: inherit;
        position: relative;
        z-index: 1;
        transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1);
        width: 100%;
    }

    .mobile-swipe-tray {
        position: absolute;
        right: 0;
        top: 0;
        bottom: 0;
        width: 80px;
        display: flex;
        align-items: stretch;
        z-index: 0;
    }

    .mobile-swipe-btn {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        flex: 1;
        border: none;
        cursor: pointer;
        color: var(--color-pure-white, #fff);
        font-size: 0.65rem;
        font-weight: 600;
        gap: 4px;
        padding: 8px 4px;
        min-width: 0;
    }

    .mobile-swipe-btn i {
        font-size: 1.1rem;
    }

    .mobile-swipe-btn-danger {
        background: var(--color-crimson, #A62C2B);
    }

    .mobile-swipe-btn-danger:active {
        background: #8a2424;
    }

    .mobile-swipe-btn-default {
        background: var(--color-charcoal, #2C3539);
    }

    .mobile-swipe-btn-default:active {
        background: #1a2023;
    }

    .mobile-selection-mode .mobile-card-inner {
        transform: none !important;
    }

    .mobile-selection-mode .mobile-swipe-tray {
        display: none;
    }

    /* The tray sits behind the card with the card's own bounds, and the card layer can end a
       pixel short of it (borders) or land on fractional pixels, so its crimson edge showed
       through as a thin line on every swipe card (document lines, invoice/quote/customer
       lists). Hide it at rest; show it while the card is dragged (.mobile-swiping, set by
       mobile_swipe_actions.js) or held open. The hide is delayed so the card finishes
       sliding back over it first. (take_on_*.css carry their own :active variants — those
       are more specific and unaffected.) */
    .mobile-list-card .mobile-swipe-tray {
        opacity: 0;
        transition: opacity 0.1s ease 0.25s;
    }

    .mobile-list-card.mobile-swiping .mobile-swipe-tray,
    .mobile-list-card.mobile-swipe-open .mobile-swipe-tray {
        opacity: 1;
        transition-delay: 0s;
    }
}

/* ===== Group A: Linear list table/card swap =====
   Mirrors the ledger-container pattern above for pages using
   the list_view.html macro system (customers, suppliers, accounts, items, bundles).
   Header hiding handled by existing body.mobile-mode rule (line 803). */
@media (max-width: 767.98px) {
    .v-linear-list .v-linear-header {
        display: none !important;
    }

    .v-linear-list .v-linear-row {
        display: none !important;
    }

    .v-linear-list .mobile-list-card {
        display: flex !important;
    }
}

/* ===== Chart of Accounts: mobile category groups =====
   Collapsed-by-default Account Type sections (Asset/Liability/Equity/Revenue/
   Cost of Sales/Expense) so a phone doesn't get one long flat scroll of every
   account. Chevron rotation mirrors the existing .v-notes-header pattern —
   both rely on Bootstrap's collapse plugin syncing aria-expanded on the
   trigger itself, no extra JS needed. */
.v-account-group-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.85rem 1.25rem;
    background: var(--crm-recessed, #fafbfd);
    border: none;
    border-bottom: 1px solid var(--crm-border, #eaedf0);
    text-align: left;
    font-weight: 600;
    color: var(--v-charcoal, #2C3539);
}

.v-account-group-chevron {
    transition: transform 0.25s ease;
    color: var(--crm-text-muted, #8e959c);
}

.v-account-group-header[aria-expanded="true"] .v-account-group-chevron {
    transform: rotate(90deg);
}

.v-account-group-title {
    flex-grow: 1;
}

.v-account-group-count {
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.15rem 0.55rem;
    border-radius: 10px;
    background: var(--crm-border, #eaedf0);
    color: var(--crm-text-muted, #8e959c);
}

/* ----------------------------------------------------------------------
   PRJ-031 B5: document-edit width + truncation polish (mobile only)
   The page was spending ~65px/side on layout padding + page-card padding
   + Bootstrap row gutters, leaving ~55% of a 304px screen for content.
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    /* <main> carries a global 16px side padding; drop it on document screens only */
    main:has(.v-transaction-layout) {
        padding-left: 0 !important;
        padding-right: 0 !important;
    }

    /* Outer layout gutter (modern_sidebar.css sets 1.5rem !important) */
    body .v-transaction-layout {
        padding-left: 0.5rem !important;
        padding-right: 0.5rem !important;
    }

    /* Framing card: use the full width, keep the white frame */
    .v-page-card-preview {
        max-width: 100%;
        margin: 0.5rem auto;
        padding: 0.5rem 0.25rem 1rem;
    }

    /* Section rows: drop the 0.75rem col gutter and tighten stacking */
    .v-page-card-preview > .row {
        --bs-gutter-x: 0.25rem;
        margin-bottom: 0.75rem !important;
    }

    /* Line Items header: let the pricing toggle wrap under the title
       instead of squeezing its label into a 3-line column */
    .hardened-card > .card-header {
        flex-wrap: wrap;
        row-gap: 0.25rem;
    }

    .hardened-card > .card-header > .d-flex {
        gap: 0.5rem !important;
    }

    .hardened-card .v-tax-toggle-wrapper > span {
        display: none; /* "Pricing Mode:" is redundant next to the switch label */
    }

    .hardened-card .v-tax-toggle-wrapper .form-check-label {
        white-space: nowrap;
    }

    /* Clear the floating action pill + chat bubble so totals are reachable */
    .v-transaction-layout {
        padding-bottom: 96px;
    }
}

/* ----------------------------------------------------------------------
   PRJ-031 Phase D1: mobile hero (replaces the one-line strip).
   Deep Canvas block: doc id + plain-text status, entity, total (+ VAT / due
   aside). Compacts on scroll via .is-shrunk (mobile_orchestrator.js).
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .mobile-hero {
        background: var(--color-charcoal);
        color: #fff;
        padding: 12px 16px 14px;
        border-radius: 0 0 12px 12px;
        box-shadow: 0 4px 14px rgba(44, 53, 57, 0.25);
        transition: padding 0.25s ease;
    }

    .mobile-hero-doc {
        display: flex;
        align-items: center;
        gap: 8px;
        font-size: 0.72rem;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: rgba(255, 255, 255, 0.6);
        font-family: 'Inter', sans-serif;
    }

    .mobile-hero-docname {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        min-width: 0;
    }

    .mobile-hero .mobile-doc-strip-entity {
        display: block;
        margin-top: 3px;
        font-size: 1.3rem;
        transition: font-size 0.25s ease;
    }

    .mobile-hero .mobile-doc-strip-entity.is-empty {
        color: var(--color-gold);
    }

    .mobile-hero-figures {
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        gap: 12px;
        margin-top: 10px;
        transition: margin 0.25s ease;
    }

    .mobile-hero-label,
    .mobile-hero-aside {
        font-size: 0.75rem;
        color: rgba(255, 255, 255, 0.6);
        max-height: 44px;
        overflow: hidden;
        transition: max-height 0.25s ease, opacity 0.2s ease;
    }

    .mobile-hero-aside {
        text-align: right;
        line-height: 1.5;
        font-family: 'Inter', sans-serif;
    }

    .mobile-hero-aside strong {
        color: #fff;
        font-weight: 600;
    }

    .mobile-hero-money {
        font-family: 'Roboto Mono', monospace;
    }

    .mobile-hero-total {
        line-height: 1.05;
        transition: font-size 0.25s ease;
        font-size: 2rem;
    }

    .mobile-hero .mobile-doc-strip-money-whole {
        font-size: 1em;
    }

    /* Big totals: the currency sits on its own line above the figure instead of wrapping off it. */
    .mobile-hero-total.is-large .mobile-hero-cur {
        display: block;
        font-size: 0.8rem;
        line-height: 1.1;
        letter-spacing: 0.08em;
        color: rgba(255, 255, 255, 0.6);
        font-family: 'Inter', sans-serif;
    }

    .mobile-hero-total .mobile-doc-strip-money-whole,
    .mobile-hero-aside .mobile-hero-money {
        white-space: nowrap;
    }

    /* The aside (VAT + key date) was clipped by the 44px cap once the VAT figure grew. */
    .mobile-hero-aside {
        max-height: 80px;
    }

    /* Compact (scrolled): drop the captions, shrink name + total */
    .mobile-sticky-portal.is-shrunk .mobile-hero {
        padding-top: 8px;
        padding-bottom: 10px;
    }

    .mobile-sticky-portal.is-shrunk .mobile-hero .mobile-doc-strip-entity {
        font-size: 1.05rem;
    }

    .mobile-sticky-portal.is-shrunk .mobile-hero-figures {
        margin-top: 2px;
    }

    .mobile-sticky-portal.is-shrunk .mobile-hero-total {
        font-size: 1.5rem;
    }

    .mobile-sticky-portal.is-shrunk .mobile-hero-total.is-large .mobile-hero-cur {
        display: inline;
        font-size: 1em;
        color: inherit;
        font-family: inherit;
    }

    .mobile-sticky-portal.is-shrunk .mobile-hero-label,
    .mobile-sticky-portal.is-shrunk .mobile-hero-aside {
        max-height: 0;
        opacity: 0;
    }
}

/* ----------------------------------------------------------------------
   PRJ-031 Phase D2: items card — segmented VAT switch, flat rows, light totals.
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    /* Section headers: one consistent small-caps treatment (Details / Line Items / Message) */
    .hardened-card > .card-header h6 {
        font-size: 0.85rem !important;
        text-transform: uppercase;
        letter-spacing: 1px;
    }

    /* The real switch stays in the DOM (popup strip moves it) but is replaced by the
       segmented control while it lives in the card header. */
    .hardened-card > .card-header .v-tax-toggle-wrapper {
        display: none;
    }

    .mobile-vat-seg {
        display: flex;
        background: #f1f3f5;
        border-radius: 10px;
        padding: 3px;
    }

    .mobile-vat-seg button {
        height: 32px;
        padding: 0 10px;
        border: 0;
        border-radius: 8px;
        background: transparent;
        font-family: 'Inter', sans-serif;
        font-size: 0.75rem;
        font-weight: 600;
        color: var(--color-text-grey);
        white-space: nowrap;
    }

    .mobile-vat-seg button[aria-pressed="true"] {
        background: #fff;
        color: var(--color-text-dark);
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
    }

    .mobile-vat-seg button:disabled {
        opacity: 0.6;
    }

    /* Line rows: flat white rows separated by hairlines (was grey cards with a dark left edge) */
    .mobile-line-item-card:not(.active-zoomed) {
        border: 0;
        border-top: 1px solid #eaedf0;
        border-radius: 0;
        box-shadow: none;
        background: #fff;
    }

    .mobile-line-item-card:not(.active-zoomed) .mobile-card-inner > .d-flex {
        background: #fff !important;
    }

    /* Totals: the (single, JS-updated) total card becomes a light block that follows the
       items card instead of a dark slab after Message / Terms. */
    .v-page-card-preview > .row.g-4.mb-5 {
        display: flex;
        flex-direction: column;
    }

    .v-page-card-preview > .row.g-4.mb-5 > .col-md-4 {
        order: -1;
        margin-bottom: 0.5rem !important;
    }

    .v-page-card-preview .card-money {
        background: #fafbfc !important;
        color: var(--color-text-dark);
    }

    .v-page-card-preview .card-money .card-body {
        display: grid !important;
        grid-template-columns: 1fr auto;
        align-items: baseline;
        text-align: left !important;
        padding: 0.75rem 1rem;
    }

    .v-page-card-preview .card-money .divider {
        grid-column: 1 / -1;
        order: 1;
        border: 0;
        margin: 0;
        padding: 0;
    }

    .v-page-card-preview .card-money .divider span,
    .v-page-card-preview .card-money .divider .text-white-50 {
        color: var(--color-text-grey) !important;
    }

    .v-page-card-preview .card-money .divider .text-white {
        color: var(--color-text-dark) !important;
    }

    .v-page-card-preview .card-money .card-money-label {
        order: 2;
        color: var(--color-text-dark);
        font-weight: 700;
        letter-spacing: 0;
        border-top: 1px solid #eaedf0;
        padding-top: 6px;
        margin-top: 6px;
    }

    .v-page-card-preview .card-money .amount-large {
        order: 2;
        color: var(--color-text-dark);
        font-size: 1rem;
        border-top: 1px solid #eaedf0;
        padding-top: 6px;
        margin-top: 6px;
    }
}

@media (max-width: 767.98px) {
    /* D2 totals polish: readable ink on the light block, normal-case label */
    .v-page-card-preview .card-money .card-money-label {
        font-size: 0.95rem;
        text-transform: none;
        font-family: 'Inter', sans-serif;
    }

    .v-page-card-preview .card-money .amount-large,
    .v-page-card-preview .card-money .amount-large-whole,
    .v-page-card-preview .card-money .amount-large-cents {
        color: var(--color-text-dark) !important;
        font-family: 'Roboto Mono', monospace;
        font-weight: 700;
        text-shadow: none;
        text-align: right;
    }

    .v-page-card-preview .card-money .divider > div {
        padding-left: 0 !important;
        padding-right: 0 !important;
    }
}

/* ----------------------------------------------------------------------
   PRJ-031 D2b: match the approved prototype — cards float on the sand
   canvas (no white page frame), hero runs edge to edge, line rows are
   truly flat (the older grey cover + 0.5em inset is neutralised).
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    body .v-transaction-layout {
        padding-left: 0 !important;
        padding-right: 0 !important;
    }

    .v-page-card-preview {
        background: transparent;
        box-shadow: none;
        border-radius: 0;
        padding: 0 0 1rem;
        margin: 0 auto;
        max-width: 100%;
    }

    /* Section rows become the floating cards, 12px from the screen edge */
    .v-page-card-preview > .row {
        --bs-gutter-x: 0;
        margin-left: 12px;
        margin-right: 12px;
        margin-top: 12px;
        margin-bottom: 0 !important;
    }

    .v-page-card-preview .card,
    .v-page-card-preview .v-sticky-header-bar {
        border: 1px solid #eaedf0;
        border-radius: 12px;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
    }

    .v-page-card-preview .v-sticky-header-bar-wrapper {
        top: 126px !important; /* below top bar (70px) + compact hero */
    }

    body body .mobile-line-item-card .mobile-card-inner,
    .mobile-line-item-card:not(.active-zoomed) .mobile-card-inner {
        background: #fff !important;
        padding-top: 0 !important;
        padding-left: 0 !important;
    }

    .mobile-line-item-card:not(.active-zoomed) .mobile-card-inner > div {
        background: #fff !important;
    }

    .mobile-line-item-card:not(.active-zoomed) {
        background: #fff !important;
    }
}

/* D2c: sentence-case section headers + rounded top corners on the items card */
@media (max-width: 767.98px) {
    .mobile-section-card .card-header h6,
    .v-sticky-header-bar h6,
    .hardened-card > .card-header h6 {
        text-transform: none !important;
        letter-spacing: 0.2px !important;
        font-size: 0.95rem !important;
    }

    /* "Line Items" is shared with desktop markup: lowercase it, then re-capitalise the first letter */
    .hardened-card > .card-header h6 {
        text-transform: lowercase !important;
    }

    .hardened-card > .card-header h6::first-letter {
        text-transform: uppercase;
    }

    .hardened-card > .card-header {
        border-top-left-radius: 12px;
        border-top-right-radius: 12px;
    }
}

/* ----------------------------------------------------------------------
   PRJ-031 Phase D3: bottom sheets. A card with .mobile-sheet renders as a
   bottom sheet while .active-zoomed (instead of scaling in place). Cards
   without the class keep the old zoom until they are migrated (D3b-d).
   Fixed positioning is relative to the viewport only if no ancestor has a
   transform/filter — the orchestrator never applies those to ancestors of
   the ACTIVE card. Footer (.zoom-action-footer, z 9999) is the shared
   action surface; sheet z stays in the existing 5000 slot.
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .mobile-sheet-scrim {
        display: none;
    }

    body.sheet-open .mobile-sheet-scrim {
        display: block;
        position: fixed;
        top: 70px; /* leave the top bar (z 2200) visible */
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 4900;
        background: rgba(26, 26, 26, 0.45);
        animation: mobileSheetFade 0.25s ease both;
    }

    body.mobile-mode .mobile-section-card.mobile-sheet.active-zoomed {
        position: fixed !important;
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        height: calc(100dvh - 78px);
        margin: 0 !important;
        padding-bottom: 110px; /* clear the shared footer */
        overflow-y: auto;
        overscroll-behavior: contain;
        transform: none !important;
        transition: none !important;
        border: 0 !important;
        border-radius: 18px 18px 0 0 !important;
        box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.25) !important;
        z-index: 5000 !important;
        animation: mobileSheetUp 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
    }

    /* Title bar: × replaces the chevron while open */
    .mobile-sheet-close {
        display: none;
        font-size: 1.1rem;
        color: var(--color-text-grey);
        padding: 4px 8px;
    }

    .mobile-sheet.active-zoomed .mobile-sheet-close {
        display: inline-block;
    }

    .mobile-sheet.active-zoomed .mobile-toggle-icon {
        display: none;
    }

    .mobile-sheet.active-zoomed > .card-header {
        position: sticky;
        top: 0;
        z-index: 2;
        border-radius: 18px 18px 0 0;
    }

    /* Message / terms: the textarea fills the sheet */
    .mobile-sheet.active-zoomed #footer-message-textarea {
        min-height: calc(100dvh - 78px - 60px - 110px) !important; /* sheet - title bar - footer clearance; beats the inline min-height */
    }
}

@keyframes mobileSheetUp {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
}

@keyframes mobileSheetFade {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* D3: the old in-place zoom needed 400px of extra scroll room; a fixed sheet does not */
@media (max-width: 767.98px) {
    body.mobile-mode.sheet-open #mobile-orchestrator-root.zoomed-context {
        padding-bottom: 96px !important; /* same as the resting value (B5c: clears the action pill) */
    }
}

/* D3: sheets must not animate margin/padding/height on the way back into flow (the shared
   `transition: all 0.4s` made the page height spike and scroll-anchoring dragged scrollY on close) */
@media (max-width: 767.98px) {
    body.mobile-mode .mobile-section-card.mobile-sheet {
        transition: none !important;
    }
}

/* D3b: Details & addresses sheet. The row itself is the sheet; its title bar lives in a
   sticky wrapper that the old zoom forced to position:static — as a sheet it should stick. */
@media (max-width: 767.98px) {
    body.mobile-mode .mobile-section-card.mobile-sheet.active-zoomed .v-sticky-header-bar-wrapper {
        position: sticky !important;
        top: 0 !important;
        z-index: 3;
    }

    .mobile-sheet.active-zoomed .v-sticky-header-bar {
        border-radius: 18px 18px 0 0 !important;
        border-width: 0 0 1px 0 !important;
        box-shadow: none !important;
    }

    /* The two-column desktop form (g-5 = 3rem gutters) is far too airy stacked in a sheet */
    .mobile-sheet.active-zoomed .v-header-body-flow .row.g-5 {
        --bs-gutter-y: 1.25rem;
        --bs-gutter-x: 0;
    }

    .mobile-sheet.active-zoomed #mobileDetailsCollapse {
        border: 0 !important;
        box-shadow: none !important;
    }

    .mobile-sheet.active-zoomed .v-header-body-flow .col-md-7 {
        border-right: 0 !important;
    }
}

/* ----------------------------------------------------------------------
   PRJ-031 D3c/D3d: line edit + add line as bottom sheets. Same shell as the
   section sheets above. Taps inside never cancel (see the card onclick);
   x / backdrop / footer Cancel do. The swipe tray is off while open.
   ---------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    body.mobile-mode .mobile-line-item-card.mobile-sheet.active-zoomed {
        position: fixed !important;
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        height: calc(100dvh - 78px);
        margin: 0 !important;
        padding: 0 0 110px;
        overflow-y: auto;
        overscroll-behavior: contain;
        transform: none !important;
        transition: none !important;
        border: 0 !important;
        border-radius: 18px 18px 0 0 !important;
        box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.25) !important;
        background: #fff !important;
        z-index: 5000 !important;
        animation: mobileSheetUp 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
    }

    .mobile-sheet-bar {
        display: none;
    }

    .mobile-sheet.active-zoomed .mobile-sheet-bar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex: 0 0 100%;
        width: 100%;
        position: sticky;
        top: 0;
        z-index: 3;
        padding: 14px 16px;
        background: #fff;
        border-bottom: 1px solid #eaedf0;
        border-radius: 18px 18px 0 0;
    }

    .mobile-sheet-bar-title {
        font-family: 'Merriweather', serif;
        font-weight: 700;
        font-size: 0.95rem;
    }

    .mobile-line-item-card.mobile-sheet.active-zoomed .mobile-card-inner {
        background: #fff !important;
        padding: 0 !important;
        width: 100%;
        min-height: 100%;
        align-content: flex-start;
        transform: none !important;
    }

    /* The row summary and the "+ Add line" label are redundant inside the open sheet */
    .mobile-line-item-card.mobile-sheet.active-zoomed .mobile-card-inner > .d-flex,
    .mobile-sheet.active-zoomed .mobile-add-line-label,
    .mobile-sheet.active-zoomed .mobile-swipe-tray {
        display: none !important;
    }

    .mobile-line-item-card.mobile-sheet.active-zoomed .mobile-line-edit-slot {
        padding: 16px;
    }
}

/* D3c: the desktop inline edit row stays behind, empty (~49px), once the form is re-parented
   into the mobile sheet — it only showed as a blank gap in the page. Mobile only. */
@media (max-width: 767.98px) {
    #inline-edit-row {
        display: none !important;
    }
}

/* D4: collapsed Details bar shows a one-line summary under the title; the open sheet shows the form instead */
@media (max-width: 767.98px) {
    .mobile-section-summary {
        font-size: 0.78rem;
        color: var(--color-text-grey);
        font-family: 'Inter', sans-serif;
        margin-top: 2px;
        padding-left: 1.35rem; /* aligns under the title, past the chevron */
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mobile-sheet.active-zoomed .mobile-section-summary {
        display: none;
    }
}

/* ===== PRJ-031 L1: list filter pills (first 3 + "more" menu) and per-page selector (mobile only) ===== */
@media (max-width: 767.98px) {
    .status-toggle-group { overflow: visible; flex-wrap: nowrap; position: relative; gap: 6px; }
    .ledger-search-bar .status-toggle-group .status-btn, .status-toggle-group .status-btn { padding: 8px 13px; }
    .status-toggle-group .status-btn-overflow { display: none; }
    .status-more { position: relative; flex-shrink: 0; }
    .status-toggle-group .status-more-btn { padding: 8px 14px; }
    .status-more-menu {
        display: none; position: fixed; z-index: 5000;
        min-width: 160px; padding: 6px; background: #fff; border: 1px solid var(--v-border, #C4C4C4);
        border-radius: 12px; box-shadow: 0 8px 24px rgba(44, 53, 57, .18);
    }
    .status-more.open .status-more-menu { display: block; }
    .status-more-item {
        display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px;
        border: 0; background: transparent; padding: 11px 12px; min-height: 44px; border-radius: 8px;
        font-size: .82rem; font-weight: 600; color: var(--v-charcoal, #2C3539); text-align: left;
    }
    .status-more-item.active { background: rgba(44, 53, 57, .07); }

    /* Page-size select: native arrow was eating the value; reserve room for it */
    .v-pagination-size select,
    .v-per-page select,
    .ledger-per-page select {
        min-width: 4.25rem;
        padding: 6px 28px 6px 12px;
        background-position: right 8px center;
        min-height: 36px;
    }
}

/* PRJ-074 mobile: the "+ Add New ..." modal (dropdown_add_new.js) opens from
   inside a bottom sheet (z 5000) / zoom footer (z 9999). Bootstrap's default
   modal (1055) and backdrop (1050) rendered BEHIND them, so the tap looked
   like it did nothing. Lift both above every mobile layer.

   PRJ-106 fix: the lift originally covered only #dropdownAddNewModal, but the
   backdrop rule is global - so every OTHER Bootstrap modal (e.g. the Chart of
   Accounts "Add Account" modal, opened from its FAB) stayed at 1055 UNDER the
   10040 backdrop and rendered dimmed and untappable. A modal must always sit
   above its own backdrop, so the modal lift is global too. Toasts (3000) are
   lifted above modals so an error raised from inside a modal stays visible. */
body.mobile-mode .modal-backdrop {
    z-index: 10040;
}
body.mobile-mode .modal {
    z-index: 10050;
}
body.mobile-mode #v-notification-container,
body.mobile-mode #v-ambient-pill-container {
    z-index: 10100;
}

/* ===== PRJ-031 L2: compact list chrome (mobile only) ===== */
@media (max-width: 767.98px) {
    /* Heading: title + count on one line */
    .v-crm-sticky-top .mobile-context-heading {
        display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
        padding: 10px 16px 8px;
    }
    .v-crm-sticky-top .mobile-context-title { font-size: 1.1rem; }
    .v-crm-sticky-top .mobile-context-subtitle { margin: 0; flex-shrink: 0; }
    .v-crm-sticky-top.is-scrolled .mobile-context-heading { display: none; }

    /* Search block: use the full width (was 30px + 8px + 4px of nested padding each side) */
    .v-crm-sticky-top .ledger-control-row { padding: 8px 12px 6px; }
    .v-crm-sticky-top .search-unit-container { padding: 0; }
    .v-crm-sticky-top .search-bar-wrapper { padding: 0; }
    .v-crm-sticky-top .search-bar-wrapper .search-input-group { margin-bottom: 6px; }
    .v-crm-sticky-top .status-toggle-group { padding: 0 0 2px; }

    /* KPI: one slim strip instead of three cards */
    .mobile-kpi-bar {
        gap: 0; padding: 8px 4px; background: #fff; border-bottom: 1px solid rgba(44, 53, 57, 0.08);
    }
    .mobile-kpi-card {
        padding: 2px 8px; border: 0; border-radius: 0; box-shadow: none; background: transparent;
        border-right: 1px solid rgba(44, 53, 57, 0.1);
    }
    .mobile-kpi-card:last-child { border-right: 0; }
    .mobile-kpi-count { display: none; }
    .mobile-kpi-value { font-size: 0.9rem; }

    /* Floating buttons step aside while scrolling down; they return on scroll up / at top */
    .mobile-list-fab, .v-feedback-trigger { transition: transform 0.2s ease, opacity 0.2s ease; }
    body.list-fabs-hidden .mobile-list-fab,
    body.list-fabs-hidden .v-feedback-trigger {
        transform: translateY(140px) !important; opacity: 0; pointer-events: none;
    }

    /* Dates: readable, not ghosted */
    .mobile-card-date-sub { opacity: 0.75; }
}

/* ===== Touch devices: no Android text-selection / callout menu on long-press =====
   Long-pressing anywhere in the app opened Android's selection toolbar
   (copy / select all / web search) and made the long-press-to-select list rows
   and swipe actions messy. Touch-only (pointer: coarse) + mobile-mode, so a
   desktop browser, or a narrow desktop window with a mouse, is unaffected.
   Anything people type into stays selectable; opt a read-only value back in
   with .v-selectable (e.g. a reference number someone needs to copy).
   Companion: static/js/touch_callout_guard.js blocks the link/image context menu. */
@media (pointer: coarse) {
    body.mobile-mode {
        -webkit-user-select: none;
        user-select: none;
        -webkit-touch-callout: none;
    }
    body.mobile-mode input,
    body.mobile-mode textarea,
    body.mobile-mode [contenteditable="true"],
    body.mobile-mode .ts-control input,
    body.mobile-mode .v-selectable,
    body.mobile-mode .v-selectable * {
        -webkit-user-select: text;
        user-select: text;
    }
}
