/* ═══════════════════════════════════════════════════════════════════════
   MERKVEX — MOBILE APP VIEW ARCHITECTURE (< 768px)
   mx_app_mobile.css · v1 · 2026-08-06

   Platform targets: Web Mobile · iOS WebView/Swift · Android WebView/Kotlin
   (React Native / Capacitor-ready layout primitives.)

   Load AFTER mx_tokens.css. Shell auto-injects this file on every page.
   Do NOT put desktop hotfixes here — desktop lives in mx_tokens / page CSS.

   Layers:
     1. App chrome  — top app bar (≤48px row) + bottom dock
     2. Screen feed — untrapped vertical scroll (main content)
     3. Floating    — #avCmp / FABs above dock (pointer-events hygiene)
     4. Sheets      — native bottom sheets for control suites
     5. Stages      — aspect-locked canvas viewports (Hideout)
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Design tokens (mobile app chrome) ───────────────────────────────── */
:root {
  --mx-app-bar-h: 48px;
  --mx-sheet-max: 75dvh;
  --mx-sheet-radius: 18px;
  /* Was a flat 42vh — same root-cause class as the --mx-fab-bottom bug
     fixed elsewhere: setting a var() unconditionally at :root means every
     var(--mx-stage-h, fallback) read downstream never sees "unset", so no
     fallback ever engages. The room art is aspect-locked to 640:420 —
     capping the default to its own width-fit height kills the dead black
     gap below the diorama in the non-editing view (confirmed via real
     mobile screenshot: gap present at plain 42vh, gone once capped).
     .ho-editing still overrides this per-state below; unaffected. */
  --mx-stage-h: min(42vh, calc(100vw * 420 / 640));
  --mx-stage-max: 360px;
  --mx-hero-max: 120px;
  /* FABs clear dock + home-indicator */
  --mx-fab-bottom: calc(var(--mx-dock-h, 0px) + 16px + env(safe-area-inset-bottom, 0px));
}

/* Standard visually-hidden utility. mx_app_mobile.js:547 emits a bare
   <label class="sr-only"> for the sticky floor search bar and this class
   was never defined anywhere in the repo — the label rendered as plain
   visible text, wrapping "Search the floor" across 3 lines and bleeding
   over the +File/+Filter buttons and card art on real mobile (confirmed
   via Playwright render on bazaar.html, 2026-08-08). my-bazaar.html's own
   sr-only spots already inline this exact CSS as a workaround; defining
   the class once here fixes every current and future bare-class usage. */
.sr-only{
  position:absolute!important;width:1px;height:1px;
  padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* ═══════════════════════════════════════════════════════════════════════
   MOBILE APP SHELL — max-width 767.98px only
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
  /* ── 0. Viewport constitution ─────────────────────────────────────── */
  html,
  body {
    overflow-x: clip !important;
    max-width: 100vw !important;
  }
  body.mx-skin,
  body.mx-app-mobile {
    max-width: 100vw;
    /* Screen feed clears the fixed dock */
    padding-bottom: calc(var(--mx-dock-h, 64px) + env(safe-area-inset-bottom, 0px) + 12px);
  }
  body.mx-has-dock {
    --mx-fab-bottom: calc(var(--mx-dock-h, 64px) + 16px + env(safe-area-inset-bottom, 0px));
  }

  /* Camera scan chip inside search inputs (mx_ds.css §search-cam sizes it
     36x36 for desktop density): 44x44 on phone — it is the primary scan
     entry point on my-bazaar/floor/market. Inputs that host it pad 48px in
     mx_ds; the bigger chip needs 58px so text never underlaps it. */
  /* #avFileSheet variant included by name: mx_ds sizes that one through an
     id-weighted selector (#avFileSheet .mx-search-cam-btn, 36px) that beats
     any class-only override regardless of source order. */
  .mx-search-with-cam .mx-search-cam-btn,
  .mx-nav-search .mx-search-cam-btn,
  .bz-floor-tools .mx-search-cam-btn,
  .mb-add-search .mx-search-cam-btn,
  .mk-bar .mx-search-cam-btn,
  #avFileSheet .mx-search-cam-btn {
    width: 44px;
    height: 44px;
    min-width: 44px;
    min-height: 44px;
  }
  .mx-search-with-cam .mx-search-with-cam__input,
  .mx-search-with-cam input,
  .mx-nav-search input.mx-search-with-cam__input,
  #searchInput.mx-search-with-cam__input,
  #mbAddCard.mx-search-with-cam__input,
  #mkSearch.mx-search-with-cam__input,
  #avFileSheet .mx-file-input--search {
    padding-inline-start: 58px !important;
  }

  /* ── 1. TOP MOBILE APP BAR ─────────────────────────────────────────
     Row 1: [Logo / status] …… [Account]. Max height 48px.
     No horizontal overflow. Search (if present) is a second full-width row
     that does not break the primary bar geometry. */
  .mx-nav {
    height: auto;
    min-height: var(--mx-app-bar-h);
    max-height: none;
    gap: 4px 6px;
    flex-wrap: wrap !important;
    align-items: center;
    overflow-x: clip !important;
    overflow-y: visible;
    padding-block: 0;
    padding-inline: max(10px, var(--mx-sal, env(safe-area-inset-left, 0px)))
      max(10px, var(--mx-sar, env(safe-area-inset-right, 0px)));
  }
  /* Primary chrome row items sit in a 48px band */
  .mx-nav-logo,
  .mx-nav-right,
  .mx-nav-links,
  .mx-nav-account-btn {
    max-height: var(--mx-app-bar-h);
  }
  .mx-nav-logo {
    flex: none;
    min-width: 0;
    max-width: min(52vw, 200px);
    overflow: hidden;
  }
  .mx-nav-word {
    font-size: 16px !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .mx-nav-mark {
    width: 28px !important;
    height: 28px !important;
    flex: none;
  }
  .mx-nav-right {
    display: flex !important;
    align-items: center;
    gap: 6px;
    flex: none;
    margin-inline-start: auto;
    min-width: 0;
    overflow: visible;
  }
  /* Sign-in parks in the account sheet. Currency chip stays in the ribbon. */
  .mx-nav-right > .mx-nav-signin {
    display: none !important;
  }
  .mx-nav-right > .mx-nav-cur {
    display: inline-block !important;
  }
  .mx-nav-account-btn {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 44px;
    min-width: 44px;
    min-height: 44px;
    height: 44px;
    padding: 0;
    border-radius: 10px;
    border: 1px solid var(--mx-line2, #313c62);
    background: var(--mx-panel2, #141a30);
    color: var(--mx-ink, #f4f6ff);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .mx-nav-account-btn[aria-expanded='true'] {
    border-color: var(--mx-mint, #7cffb2);
    color: var(--mx-mint, #7cffb2);
  }
  /* Kill overflow from primary link ribbon on phone (dock carries destinations) */
  .mx-nav-links {
    display: none !important;
  }
  #mx-nav {
    min-height: calc(var(--mx-app-bar-h) + var(--mx-sat, env(safe-area-inset-top, 0px)));
    position: sticky;
    top: 0;
    z-index: var(--mx-z-nav, 50);
  }

  /* ── 2. PRIMARY CONTENT FEED ───────────────────────────────────────
     Untrapped vertical scroll. Sticky sub-bars sit under measured nav.
     Forms / bulk tools belong in document flow or bottom sheets — not
     fixed traps that steal viewport. */
  main,
  main#main,
  .mx-app-feed,
  [data-mx-feed] {
    min-width: 0;
    max-width: 100vw;
    overflow-x: clip;
    -webkit-overflow-scrolling: touch;
  }
  #mbAddBar,
  .sec-nav,
  body.mx-skin.ws-page .sec-nav,
  body.ws-page .sec-nav,
  [data-mx-sticky],
  .mx-sticky-under-nav {
    top: var(--mx-sticky-top, var(--mx-nav-h, 96px)) !important;
    z-index: calc(var(--mx-z-nav, 50) - 5) !important;
  }

  /* ── 3. FLOATING INTERACTIVE LAYER (#avCmp + FABs) ─────────────────
     Wolf / compare widget anchors above the dock. Transparent hosts never
     steal taps from the dock or feed.
     Mobile-audit find (2026-08-08): .ho-fab-menu/.ho-fab-save/
     .ho-fab-menu-panel used to be in this list too, forcing
     bottom:var(--mx-fab-bottom) with NO fallback and !important — this is
     hideout.html's OWN chrome (its bottom bar is a bespoke ho-thumb-bar,
     not the site dock this rule was built for), and --mx-fab-bottom
     defaults to 16px sitewide whenever no real dock is measured, which is
     always true on hideout.html. Result: hideout's "Save room" and "Menu"
     buttons rendered at bottom:16px, entirely hidden under its own
     57px-tall thumb bar — confirmed via elementFromPoint, both buttons
     completely unclickable. hideout.html already computes its own
     correct --ho-botchrome value (dock+thumb+preview bar heights,
     measured live) and now positions its FABs off that directly instead
     of this generic rule. Left every other selector here untouched —
     those pages' FABs correctly want the site-dock-aware value. */
  #avCmp,
  .mx-floor-fab,
  .shop-toast,
  [data-mx-fab],
  .mx-fab,
  .mx-pull-chip {
    bottom: var(--mx-fab-bottom) !important;
  }
  /* Shop checkout tabbar sits ABOVE the global dock (no dual-chrome collision) */
  .shop-tabbar {
    bottom: calc(var(--mx-dock-h, 64px) + env(safe-area-inset-bottom, 0px) + 8px) !important;
    left: 50% !important;
    transform: translateX(-50%) !important;
    z-index: 90 !important;
    width: min(560px, calc(100vw - 24px)) !important;
  }
  #avCmp,
  [data-mx-fab],
  .mx-fab-host {
    pointer-events: none !important;
  }
  #avCmp .cmp-chip,
  #avCmp .cmp-panel,
  #avCmp .cmp-x,
  #avCmp .cmp-clear,
  #avCmp button,
  #avCmp a,
  [data-mx-fab] > *,
  .mx-fab-host > *,
  .mx-floor-fab,
  .ho-fab-menu,
  .ho-fab-save,
  .shop-tabbar {
    pointer-events: auto !important;
  }

  /* ── 4. TOUCH + SAFARI ZOOM FLOOR ────────────────────────────────── */
  body.mx-skin input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='hidden']),
  body.mx-skin select,
  body.mx-skin textarea,
  body.mx-app-mobile input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='hidden']),
  body.mx-app-mobile select,
  body.mx-app-mobile textarea,
  .mx-sheet input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='hidden']),
  .mx-sheet select,
  .mx-sheet textarea,
  .mx-nav-search-input {
    font-size: 16px !important;
  }
  body.mx-skin input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='hidden']),
  body.mx-skin select,
  body.mx-skin textarea {
    min-height: 44px;
  }

  /* ── 5. GENERIC BOTTOM SHEET (MX.ui.openSheet) ─────────────────────
     Host is full-screen for scrim hit-testing, but pointer-events only on
     scrim + panel so we never freeze the page with a hard touch lock. */
  .mx-sheet,
  #mxMapSheetFallback,
  #mxSheet-map {
    display: none;
    position: fixed;
    inset: 0;
    left: 0 !important;
    right: 0 !important;
    width: 100% !important;
    max-width: 100vw !important;
    margin: 0 !important;
    padding: 0 !important;
    z-index: calc(var(--mx-z-overlay, 90) + 50);
    pointer-events: none; /* transparent host does not steal; children re-enable */
    background: transparent;
    touch-action: manipulation;
    -webkit-user-select: auto;
    user-select: auto;
  }
  .mx-sheet.is-open,
  #mxMapSheetFallback.is-open,
  #mxSheet-map.is-open {
    display: block;
  }
  .mx-sheet__scrim,
  .mx-sheet-backdrop {
    position: absolute;
    inset: 0;
    left: 0 !important;
    right: 0 !important;
    width: 100% !important;
    height: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0;
    background: rgba(4, 5, 11, 0.62);
    cursor: pointer;
    pointer-events: auto !important;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }
  .mx-sheet__panel {
    position: absolute;
    left: 0 !important;
    right: 0 !important;
    /* Rest ON TOP of the nav dock, not under it. The dock is z-index 150 and
       the sheet host is 140 — the dock outranks sheets on purpose, because
       tapping it is the documented dismiss-and-navigate path (see
       dismissAllOverlays / p1.dock.closeAllSheets). With bottom:0 the last
       61px of every sheet therefore rendered UNDERNEATH the dock: measured
       on import.html @390x844, the final button of a sheet sat at y=796-844
       and hit-tested to `mx-dock-lbl`, i.e. it was physically untappable.
       --mx-dock-h is 0 on pages with no dock, where this collapses to 0. */
    bottom: var(--mx-dock-h, 0px) !important;
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    margin-inline-start: 0 !important;
    margin-inline: 0 !important;
    max-height: var(--mx-sheet-max, 75dvh);
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 10px 16px calc(20px + env(safe-area-inset-bottom, 0px));
    padding-inline-start: 16px !important;
    padding-inline-end: 16px !important;
    border-radius: var(--mx-sheet-radius, 18px) var(--mx-sheet-radius, 18px) 0 0;
    background: var(--mx-panel, #0f1324);
    border: 1px solid var(--mx-line2, #313c62);
    border-bottom: 0;
    box-shadow: 0 -20px 60px rgba(0, 0, 0, 0.55);
    transform: translateY(0);
    pointer-events: auto !important;
    touch-action: pan-y;
    box-sizing: border-box;
  }
  /* Map sheet: single full-width column (no split / dead right gutter) */
  .mx-sheet--map .mx-sheet__panel,
  .mx-sheet-map .mx-sheet__panel,
  #mxSheet-map .mx-sheet__panel,
  #mxMapSheetFallback .mx-sheet__panel,
  [data-mx-sheet='map'] .mx-sheet__panel {
    max-height: 75dvh !important;
    width: 100% !important;
    max-width: 100vw !important;
    left: 0 !important;
    right: 0 !important;
    inset-inline: 0 !important;
    margin: 0 !important;
    margin-inline: 0 !important;
    padding: 10px 16px calc(20px + env(safe-area-inset-bottom, 0px)) !important;
    padding-inline: 16px !important;
    border-radius: 18px 18px 0 0 !important;
    display: flex !important;
    flex-direction: column !important;
    box-sizing: border-box !important;
    /* Bug fix, corrected (Sam screenshot 2026-08-09: Collector Zone /
       Coming Later still unreachable, confirmed via a live debug banner
       that the JS built all 3 lanes correctly -- this was never missing
       content, only unreachable content). The first attempt at this fix
       set this to overflow:hidden and made .mx-sheet__body (a flex child)
       responsible for scrolling instead. That was wrong: a flex child
       only becomes a real internal scroll container when its PARENT has
       a definite height, not just a max-height cap on auto-sizing -- with
       no definite height here, .mx-sheet__body never got constrained
       small enough to have anything to scroll, so the debug build simply
       clipped everything past ~75dvh with no way to reach it (confirmed
       by the same screenshot: the page's own content was visible directly
       below the cut-off list, meaning the scrim/panel box itself ended
       there instead of extending to cover it). Reverting to overflow:auto
       on THIS element -- max-height + overflow:auto on the same box is a
       plain, universally-supported scroll pattern with none of the
       flex-child ambiguity above. */
    overflow-y: auto !important;
    overflow-x: hidden !important;
    -webkit-overflow-scrolling: touch;
  }
  .mx-sheet--map .mx-sheet__body,
  .mx-sheet-map .mx-sheet__body,
  #mxSheet-map .mx-sheet__body,
  #mxMapSheetFallback .mx-sheet__body,
  [data-mx-sheet='map'] .mx-sheet__body {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    min-height: 0 !important;
    flex: 1 1 auto !important;
    /* Panel (above) is now the single scroll container -- this stays
       overflow:visible so there's exactly one scrollable box, not two
       nested ones fighting over the same touch-drag gesture. */
    overflow: visible !important;
    padding: 0 !important;
    margin: 0 !important;
    box-sizing: border-box !important;
    columns: auto !important;
    column-count: 1 !important;
    grid-template-columns: none !important;
  }
  .mx-sheet--map .mx-map-sheet,
  #mxSheet-map .mx-map-sheet,
  [data-mx-sheet='map'] .mx-map-sheet,
  [data-mx-map-sheet] {
    display: flex !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    gap: 14px !important;
    box-sizing: border-box !important;
    columns: auto !important;
    column-count: 1 !important;
    grid-template-columns: none !important;
  }
  .mx-sheet--map .mx-map-sheet__group,
  .mx-sheet--map .mx-map-sheet__link,
  [data-mx-map-sheet] .mx-map-sheet__group,
  [data-mx-map-sheet] .mx-map-sheet__link {
    display: flex !important;
    flex-direction: column !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
    float: none !important;
  }
  .mx-sheet.is-open .mx-sheet__panel {
    animation: mx-sheet-up 0.28s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  @keyframes mx-sheet-up {
    from {
      transform: translateY(100%);
    }
    to {
      transform: translateY(0);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    .mx-sheet.is-open .mx-sheet__panel {
      animation: none;
    }
  }
  .mx-sheet__grip {
    width: 40px;
    height: 4px;
    border-radius: 99px;
    background: var(--mx-line2, #313c62);
    margin: 4px auto 10px;
  }
  .mx-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin: 0 0 10px;
    min-width: 0;
  }
  .mx-sheet__h {
    font: 800 15px/1.2 'Archivo', system-ui, sans-serif;
    color: var(--mx-ink, #f4f6ff);
    margin: 0;
    flex: 1;
    min-width: 0;
  }
  .mx-sheet__x,
  .mx-sheet-close {
    flex: none;
    width: 44px;
    height: 44px;
    min-width: 44px;
    min-height: 44px;
    margin: 0;
    padding: 0;
    border: 1px solid var(--mx-line2, #313c62);
    border-radius: 12px;
    background: var(--mx-panel2, #141a30);
    color: var(--mx-ink2, #aeb6d6);
    font: 600 22px/1 system-ui, sans-serif;
    cursor: pointer;
    pointer-events: auto !important;
    -webkit-tap-highlight-color: transparent;
  }
  .mx-sheet__x:active,
  .mx-sheet-close:active {
    color: var(--mx-ink, #f4f6ff);
    border-color: var(--mx-mint, #7cffb2);
  }
  .mx-sheet__body {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
    width: 100%;
    margin: 0;
    padding: 0;
  }
  /* Soft scroll lock only — never touch-action:none / user-select:none on body */
  body.mx-sheet-open {
    overflow: hidden;
    touch-action: manipulation;
    -webkit-user-select: auto !important;
    user-select: auto !important;
  }
  /* One search per surface. Pages that own a search hide the chrome field. */
  body[data-mx-owns-search] .mx-nav-search,
  body[data-mx-owns-search] .mx-nav-search-input {
    display: none !important;
  }
  /* Peek / zoom / any overlay — lock the feed behind it (Sam 2026-08-14). */
  html.mx-modal-lock,
  body.mx-modal-lock {
    overflow: hidden !important;
    overscroll-behavior: none;
  }
  body.mx-modal-lock .mx-screen-feed,
  body.mx-modal-lock main#main,
  body.mx-modal-lock #main {
    overflow: hidden !important;
  }
  body.mx-sheet-open * {
    -webkit-user-select: auto;
    user-select: auto;
  }
  /* Dock stays above scrim so Map can toggle-close while sheet is open */
  body.mx-sheet-open .mx-dock,
  body.mx-sheet-open #avDock {
    z-index: calc(var(--mx-z-overlay, 90) + 60) !important;
    pointer-events: auto !important;
  }

  /* Account sheet shares the same native geometry */
  .mx-account-sheet__panel {
    max-height: min(75dvh, 640px) !important;
    border-radius: var(--mx-sheet-radius, 18px) var(--mx-sheet-radius, 18px) 0 0 !important;
  }

  /* ── Legacy modals → bottom sheet geometry (100% coverage fallback) ──
     Any popup marked data-mx-sheet-ready OR using known overlay classes
     docks to the bottom on phone. JS adapter stamps data-mx-sheet-ready.
     Phase 3 (App-First, 2026-08-08): ran a real Playwright inventory at
     390x844 across all 34 pages checking ACTUAL computed border-radius +
     max-height on every position:fixed/absolute dialog-shaped element's
     children (not re-guessing selector text) — 52 already correctly sized,
     only one genuine gap: .import-overlay/.import-modal (player.html,
     added below), same centered-modal shape as everything else here, just
     never added to this list. Reviewed and deliberately NOT added — these
     looked like candidates but are correct as-is: .mx-account-sheet (own
     dedicated CSS + max-height, mx_tokens.css:3038, not a legacy pattern
     at all), .shop-drawer (own complete slide-in-from-side implementation,
     a different intentional pattern, not a modal), #shopDialogue
     (merchant narrative text box — regex false-positive on "dialogue"),
     #avVendorWall (full-screen paywall with its own inline overflow
     handling — bottom-anchoring would break its centered layout),
     #cardArtModal (player.html card-art lightbox — bottom-sheet docking
     would crop/squish the centered zoom-viewer image, wrong for this
     content shape). ───────────────────────────────────────────────── */
  .modal-bg,
  .overlay,
  body.bz-page .modal-bg,
  .mx-skin .modal-bg,
  .ho-modal-bg,
  .cd-modal,
  .cable-modal,
  .receipt-overlay,
  .fl-lb,
  .hm-lb,
  .mk-lb,
  .mb-modal,
  .mx-code-modal,
  .mx-pull-modal,
  #mbClaim,
  #pfInspect,
  #avPal,
  #avFileSheet,
  #avBinderModal,
  #avGlyphTerm,
  #mxCheckoutIssue,
  #shopDetailLb,
  .import-overlay,
  [data-mx-sheet-ready],
  [data-mx-overlay] {
    align-items: flex-end !important;
    justify-content: center !important;
    padding: 0 !important;
    padding-bottom: env(safe-area-inset-bottom, 0px) !important;
  }
  .modal-bg .modal,
  .overlay .modal,
  body.bz-page .modal-bg .modal,
  .mx-skin .modal,
  .ho-modal-bg .ho-modal,
  .ho-modal-bg > div,
  .cd-modal .cd-modal-panel,
  .cd-modal > .cd-card,
  .cd-modal > div,
  .cable-modal > div,
  .receipt-overlay > .receipt,
  .receipt-overlay > div,
  .fl-lb > .fl-lb-card,
  .fl-lb > div,
  .hm-lb > div,
  .mk-lb > div,
  .mb-modal > div,
  .mx-code-modal > div,
  .mx-pull-modal > div,
  #mbClaim > div,
  #pfInspect > div,
  #avPal > div,
  #avFileSheet > .mx-file-panel,
  #avBinderModal > div,
  #avGlyphTerm > div,
  #mxCheckoutIssue > div,
  #shopDetailLb > div,
  #shopDetailLb .in,
  .import-overlay > .import-modal,
  /* :not(.mx-sheet__panel) on every generic branch below. The native sheet
     panel is `<div class="mx-sheet__panel" role="dialog" aria-modal="true">`,
     so it matches these catch-alls — and it already carries its own complete
     geometry, including the dock offset. Without the exclusion it takes the
     dock offset TWICE (own `bottom:61px` + this rule's `margin-bottom:61px`)
     and every native sheet floats 61px too high: measured 722 instead of
     783 on import.html @390x844. */
  [data-mx-sheet-ready] > [role='dialog']:not(.mx-sheet__panel),
  [data-mx-sheet-ready] > .modal,
  [data-mx-sheet-ready] > .in,
  [data-mx-sheet-ready] > .panel,
  [data-mx-overlay] > [role='dialog']:not(.mx-sheet__panel),
  [data-mx-overlay] > .modal,
  /* :not(.mx-sheet) — this catch-all was swallowing the GENERIC sheet host.
     See the note below the block for what that broke. */
  [data-mx-overlay] > div:last-child:not(.mx-sheet):not(.mx-sheet__panel) {
    width: 100% !important;
    max-width: 100% !important;
    max-height: var(--mx-sheet-max, 92dvh) !important;
    border-radius: var(--mx-sheet-radius, 18px) var(--mx-sheet-radius, 18px) 0 0 !important;
    /* Bottom margin clears the nav dock, for the same reason
       .mx-sheet__panel uses `bottom: var(--mx-dock-h)`: the dock is
       z-index 150 and outranks every sheet, so any sheet pixel below the
       dock top is covered and untappable. Measured on profile.html
       @390x844: the bridged pickers docked to y=844 with the dock starting
       at 783, hiding their last 61px. 0 on pages with no dock. */
    margin: 0 0 var(--mx-dock-h, 0px) !important;
    overflow: auto !important;
    -webkit-overflow-scrolling: touch;
  }

  /* The legacy-modal bridge above also matches the GENERIC sheet host, via
     `[data-mx-overlay] > div:last-child`, and clamps it with
     `max-height: var(--mx-sheet-max) !important`. That variable is 75dvh
     (§1) — the `92dvh` fallback in that rule is dead, the token always
     resolves — so the full-screen scrim host was being capped to 75% of the
     viewport. `.mx-sheet__panel` is `bottom:0` INSIDE that shortened host,
     so a "bottom sheet" rendered floating ~25dvh above the bottom of the
     screen, with the page showing through, unscrimmed, underneath it.

     Measured on import.html @390x844: host height 633px (= 75dvh), panel
     bottom edge at y=633 with 211px of live page below it.

     This is almost certainly the Map-sheet bug logged 2026-08-09 as root
     cause never found: that host measured 563px on a 751px viewport, which
     is the same 75.0% ratio. The rule was missed then because the matcher
     used did not recurse into @media blocks, and this rule lives inside the
     ≤767.98px query.

     The host is a transparent hit-target for the scrim, never a sized
     surface — it must never carry a height cap. The panel keeps its own
     75dvh cap, which is the intended sheet height.

     Fixed at the source, by excluding .mx-sheet from the catch-all above.
     A trailing override was tried first and lost the cascade:
     `[data-mx-overlay] > div:last-child` is (0,2,1) and beats a plain
     `.mx-sheet` (0,1,0) even with both marked !important. This belt-and-
     braces rule is kept for the two id'd map hosts, which the catch-all
     can still reach through their own selectors. */
  .mx-sheet,
  #mxMapSheetFallback,
  #mxSheet-map {
    max-height: none !important;
  }

  /* Universal form inputs — Safari zoom kill on every template */
  input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='hidden']):not([type='file']),
  select,
  textarea {
    font-size: 16px !important;
  }

  /* Secondary list cards → item contract when unmarked */
  table.mx-table tbody tr,
  .list-row,
  .row-item,
  [data-mx-list-row] {
    min-width: 0;
  }

  /* ── 6. HIDEOUT CANVAS STAGE ───────────────────────────────────────
     Aspect-locked viewport stage at top of screen. Controls / drawers /
     action pills live BELOW the stage (scroll or sheet). Zero letterbox. */
  body.ho-mobile:not(.ho-land) .ho-stage,
  body.mx-app-mobile.ho-mobile:not(.ho-land) .ho-stage {
    width: 100% !important;
    border-radius: 0 !important;
    overflow: hidden !important;
    position: relative !important;
    background: var(--mx-void, #04050b) !important;
    height: var(--mx-stage-h, 42vh) !important;
    max-height: var(--mx-stage-max, 360px) !important;
    min-height: 0 !important;
    aspect-ratio: 640 / 420;
  }
  body.ho-mobile:not(.ho-land) .ho-stage-sec {
    padding-inline: 0 !important;
    max-width: none !important;
  }
  /* Controls under stage: normal document flow / sticky tray, not trapped */
  body.ho-mobile:not(.ho-land) .ho-stage-foot,
  body.ho-mobile:not(.ho-land) .ho-sticky-actions,
  body.ho-mobile:not(.ho-land) #hoChips {
    position: relative;
    width: 100%;
    max-width: 100vw;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* ── 7. MY BAZAAR — compact search + sheet trigger ───────────────── */
  body.mx-app-mobile #mbAddBar.mx-app-compact .mb-app-compact-row {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
  }
  body.mx-app-mobile #mbAddBar.mx-app-compact .mb-app-compact-row .mb-add-search {
    flex: 1;
    min-width: 0;
    flex-direction: row !important;
    flex-wrap: nowrap;
    align-items: center;
  }
  body.mx-app-mobile #mbAddBar.mx-app-compact .mb-app-compact-row .mb-add-search input {
    flex: 1;
    min-width: 0 !important;
    width: auto !important;
  }
  body.mx-app-mobile #mbAddBar.mx-app-compact .mb-app-compact-row #mbScanDoor {
    flex: none;
    width: auto !important;
    min-width: 44px;
    padding: 0 12px !important;
  }
  body.mx-app-mobile #mbAddBar.mx-app-compact .mx-app-sheet-trigger {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 44px;
    min-width: 44px;
    padding: 0 14px;
    border-radius: 12px;
    border: 1px solid var(--mx-line2, #313c62);
    background: var(--mx-panel2, #141a30);
    color: var(--mx-mint, #7cffb2);
    font: 700 13px 'Archivo', system-ui, sans-serif;
    cursor: pointer;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
  }
  body.mx-app-mobile #mbAddBar.mx-app-compact .mx-app-sheet-trigger[aria-expanded='true'] {
    border-color: var(--mx-mint, #7cffb2);
    box-shadow: 0 0 0 1px rgba(124, 255, 178, 0.25);
  }
  /* Hide stacked control suites when compact; keep live add flow in the feed */
  body.mx-app-mobile #mbAddBar.mx-app-compact > .mx-container > :not(.mb-app-compact-row):not(#mbSearchGrid):not(#mbPriceRow):not(#mbProdFields):not(#mbAttrs):not(#mbRecents):not(.mb-price-row) {
    display: none !important;
  }
  body.mx-app-mobile #mbAddBar.mx-app-compact #mbPriceRow.is-on,
  body.mx-app-mobile #mbAddBar.mx-app-compact .mb-price-row.is-on {
    display: flex !important;
  }
  /* Stall hero band: keep identity, don't eat the fold */
  body.mx-app-mobile .mb-stall-band {
    min-height: 0;
  }
  body.mx-app-mobile .mb-stall-band__inner {
    padding-top: 16px !important;
  }

  /* ── 8. STYLE SHOP — merchant hero ≤120px ────────────────────────── */
  body.mx-app-mobile .shop-counter,
  .shop-app .shop-counter {
    max-height: var(--mx-hero-max, 120px) !important;
  }
  body.mx-app-mobile .shop-counter__scene,
  .shop-app .shop-counter__scene {
    min-height: 0 !important;
    max-height: 100px !important;
    padding-top: 36px !important;
  }
  body.mx-app-mobile .shop-merch img,
  .shop-app .shop-merch img {
    max-height: 72px !important;
    width: clamp(64px, 22vw, 96px) !important;
  }
  body.mx-app-mobile .shop-counter__dialogue,
  .shop-app .shop-counter__dialogue {
    max-height: 2.4em;
    overflow: hidden;
    font-size: 10px !important;
    padding: 5px 8px !important;
  }
  body.mx-app-mobile .shop-counter__slab,
  .shop-app .shop-counter__slab {
    height: 18px !important;
  }
  body.mx-app-mobile .shop-counter__sign h1,
  .shop-app .shop-counter__sign h1 {
    font-size: 13px !important;
  }

  /* ── 9. CAPACITOR / WEBVIEW PREP ─────────────────────────────────── */
  body.mx-native,
  body.mx-capacitor {
    /* Native shells often own the status bar; keep content out of notch */
    padding-top: 0;
  }
  body.mx-native #mx-nav,
  body.mx-capacitor #mx-nav {
    padding-top: var(--mx-sat, env(safe-area-inset-top, 0px));
  }

  /* ── 10. DOCK STACKING + HIT TARGETS (Map click + avatar bleed) ───
     Dock must sit above avatar card glows / filter shadows (acard frame
     uses z-index up to 100 inside isolate; dock needs page-level 100+). */
  .mx-dock,
  nav.mx-dock,
  #avDock.mx-dock,
  #avDock {
    z-index: 100 !important;
    pointer-events: auto !important;
  }
  .mx-dock-item,
  .mx-dock-item * {
    pointer-events: auto !important;
  }
  /* Page scroll content below dock */
  main,
  main#main,
  .pf-wrap,
  .pf-hero,
  .pf-card-col,
  .pf-back-col,
  .pf-coins-col,
  .pf-head,
  .mb-stall-band,
  .shop-stage {
    position: relative;
    z-index: 10;
  }
  /* Avatar card: contain internal z-index / glow so it never paints over dock */
  .mx-acard,
  .pf-card-btn .mx-acard,
  .pf-card-col .mx-acard,
  #pfCard,
  #pfCardBack {
    position: relative;
    z-index: 10 !important;
    isolation: isolate;
  }
  .mx-acard::before,
  .mx-acard::after,
  .pf-card-btn::before,
  .pf-card-btn::after {
    z-index: 0 !important;
  }
  /* filter creates a stacking context — keep it under dock */
  .pf-card-btn:hover .mx-acard,
  .pf-wall-fan {
    filter: none;
  }
  @media (hover: hover) and (pointer: fine) {
    .pf-card-btn:hover .mx-acard {
      filter: drop-shadow(0 16px 36px rgba(124, 255, 178, 0.18));
    }
  }

  /* ── 11. MAP SHEET (dock Map → MX.ui.openSheet('map')) ──────────── */
  .mx-map-sheet {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
  }
  .mx-map-sheet__group {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
  }
  .mx-map-sheet__head {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px 4px 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    margin-bottom: 4px;
  }
  .mx-map-sheet__k {
    font: 700 11px/1.2 'JetBrains Mono', monospace;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--lane-c, var(--mx-mint, #7cffb2));
  }
  .mx-map-sheet__hint {
    font: 500 11px/1.3 'Archivo', system-ui, sans-serif;
    color: var(--mx-ink3, #8b94b8);
  }
  .mx-map-sheet__link {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 48px;
    padding: 10px 12px;
    border-radius: 12px;
    text-decoration: none;
    color: var(--mx-ink, #f4f6ff);
    background: transparent;
    border: 1px solid transparent;
    transition:
      background 0.18s cubic-bezier(0.16, 1, 0.3, 1),
      border-color 0.18s ease,
      transform 0.16s ease;
    -webkit-tap-highlight-color: transparent;
  }
  .mx-map-sheet__link:active {
    transform: scale(0.98);
    background: rgba(124, 255, 178, 0.08);
    border-color: rgba(124, 255, 178, 0.22);
  }
  .mx-map-sheet__label {
    font: 700 15px/1.2 'Archivo', system-ui, sans-serif;
    display: inline-flex;
    align-items: center;
    gap: 7px;
  }
  /* Mail unread count pill (Sam 2026-08-09). Painted by mx_mailbox.js's
     existing Mail.paintBadge() via [data-mx-mail-badge] -- that function
     sets textContent to the actual unread count ("3", "9+"), not just a
     dot, so this has to be a real numbered pill, not a plain color dot.
     Same shape/violet as .mx-nav-mail-badge (mx_tokens.css), the existing
     desktop-nav Mail badge -- kept consistent rather than inventing a
     second badge language. [hidden] is the resting (no unread) state. */
  .mx-map-sheet__badge {
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--mx-violet, #a98bff);
    color: var(--mx-void, #04050b);
    font: 700 9px 'JetBrains Mono', monospace;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0 10px rgba(169, 139, 255, 0.45);
    flex: none;
  }
  .mx-map-sheet__badge[hidden] {
    display: none;
  }
  .mx-map-sheet__sub {
    font: 500 12px/1.3 'Archivo', system-ui, sans-serif;
    color: var(--mx-ink3, #8b94b8);
  }

  /* ── 12. MOTION POLISH (<768) ─────────────────────────────────────── */
  .mx-dock-item {
    transition:
      color 0.16s ease,
      background 0.16s ease,
      transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .mx-sheet__panel {
    will-change: transform;
  }
  .mx-item-card,
  .shop-feat,
  .shop-row,
  .mb-lrow,
  .mb-offer {
    transition:
      transform 0.2s cubic-bezier(0.16, 1, 0.3, 1),
      border-color 0.2s ease,
      box-shadow 0.25s ease;
  }
  /* Generic sheet-trigger base (Phase 5, App-First): the class name always
     implied a shared component, but the only visual definition that existed
     was scoped under #mbAddBar.mx-app-compact (my-bazaar's own compact row)
     — every other page using this class would have rendered an unstyled
     button. Un-scoped so market/ev/flip/sets/all-access can reuse it
     directly instead of each inventing its own one-off trigger style. */
  .mx-app-sheet-trigger {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 44px;
    min-width: 44px;
    padding: 0 14px;
    border-radius: 12px;
    border: 1px solid var(--mx-line2, #313c62);
    background: var(--mx-panel2, #141a30);
    color: var(--mx-mint, #7cffb2);
    font: 700 13px 'Archivo', system-ui, sans-serif;
    cursor: pointer;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
    transition:
      transform 0.16s ease,
      border-color 0.18s ease,
      box-shadow 0.18s ease;
  }
  .mx-app-sheet-trigger[aria-expanded='true'] {
    border-color: var(--mx-mint, #7cffb2);
    box-shadow: 0 0 0 1px rgba(124, 255, 178, 0.25);
  }
  .mx-app-sheet-trigger:active {
    transform: scale(0.97);
  }
  /* Subtle enter for feed sections */
  main > section,
  main .mx-container > section,
  .pf-wrap > section {
    animation: mx-feed-in 0.36s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  @keyframes mx-feed-in {
    from {
      opacity: 0;
      transform: translateY(8px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    main > section,
    main .mx-container > section,
    .pf-wrap > section,
    .mx-sheet.is-open .mx-sheet__panel {
      animation: none !important;
    }
    .mx-dock-item:active,
    .mx-map-sheet__link:active,
    .mx-app-sheet-trigger:active {
      transform: none !important;
    }
  }

  /* ── 13. RTL / logical safety (WebKit) ────────────────────────────── */
  html[dir='rtl'] .mx-dock,
  html.mx-rtl .mx-dock {
    direction: rtl;
  }
  html[dir='rtl'] .mx-map-sheet__link,
  html.mx-rtl .mx-map-sheet__link {
    text-align: start;
  }

  /* ── 14. CONTENT BOX DENSITY — shared panel/card/tile padding + grid gap
     (Sam 2026-08-08: "the boxes things are in... data/tool density... doesn't
     feel optimized as built for mobile"). Root cause: .panel/.pcard/.mx-tile
     desktop padding (20-24px, defined across merkvex_core.css) has never had
     a mobile override anywhere in the codebase — every panel on every page
     renders at full desktop padding on a 390px screen. Values match the
     16px/14px mobile card padding already shipped on index.html's .hm-plan
     (2026-08-08 Founding density pass) rather than inventing new numbers.
     !important is required, not just convention here: stylesheet load order
     is inconsistent across pages (merkvex_core.css loads AFTER this file on
     ev.html, BEFORE it on dashboard.html/all-access.html) — a plain rule
     would win the cascade on some pages and silently lose on others. This
     covers the SHARED box vocabulary only; page-specific bespoke grids
     (e.g. dashboard's own widget layout) are a separate follow-up. ───── */
  .panel,
  .pcard,
  .mx-tile {
    padding: 16px 14px !important;
  }
  .card-result {
    padding: 10px 12px !important;
    gap: 8px !important;
  }
  .buylist-grid,
  .me-grid,
  .mx-grid {
    gap: 12px !important;
  }

  /* ── 15. TOUCH TARGETS + TACTILE FEEDBACK (App-First Standard, AGENTS.md 9b)
     Three icon-only controls in merkvex_core.css never had a mobile override:
     .btn-remove 28px (collector.html watchlist row), .btn-count 26px
     (player.html deck stepper), .modal-x 36px (shared modal close). Same
     cross-file cascade-order risk as section 14 (merkvex_core.css loads
     after this file on some pages, before it on others) — !important
     required for the same documented reason, not just convention.
     Deliberately narrow, not a blanket .btn-*/[role=button] pass: checked
     first and found .mx-dock-item/.mx-sheet__x/.mx-app-sheet-trigger
     already carry their own tap-highlight + :active feedback (mx_tokens.css
     :361, this file :418/:826) — duplicating them here would either be dead
     weight or, for .btn-primary/.btn-gate/.btn-teal-sm's existing
     translateY+scale :active states (merkvex_core.css:2046), collide at
     equal specificity with the same load-order landmine. .mx-dock-item was
     missing touch-action:manipulation specifically — added below, nothing
     else touched. A sitewide feedback audit of untouched .btn-* classes is
     real future work but needs its own per-page visual check. ─────────── */
  .btn-remove,
  .btn-count,
  .modal-x {
    min-width: 44px !important;
    min-height: 44px !important;
  }
  /* sets.html Phase 5 finding: this page's top search bar has no separate
     filter row to park into a sheet (.sc-pick is just search + a count
     label, already 44px) — the real gap here was .sc-filters button
     (ALL/MISSING/OWNED, sets.html:72) at min-height:36px. Fixing the
     actual gap instead of forcing an unneeded sheet-trigger pattern. */
  .sc-filters button {
    min-height: 44px !important;
  }
  /* all-access.html Phase 5 finding: .search-gold/.pos-search (Buylist
     Matcher / POS terminal) compute height from padding+line-height alone
     (merkvex_core.css:1020,1046 — 12-13px padding, 14px font, no explicit
     height) — borderline 42-46px depending on font metrics and blocked
     from live re-measurement by this page's Vendor Pro paywall gate in
     the QA harness. Guaranteeing the floor explicitly rather than leaving
     it ambiguous; harmless if it was already ≥44px. */
  .search-gold,
  .pos-search {
    min-height: 44px !important;
  }
  .btn-remove,
  .btn-count,
  .modal-x,
  .mx-dock-item {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
  }
  .btn-remove:active,
  .btn-count:active,
  .modal-x:active {
    transform: scale(0.97);
  }
  /* collector.html watchlist dock-overlap fix (found via Visual Veto,
     confirmed with document.elementFromPoint — a short watchlist's last
     row rendered its .btn-remove directly under the fixed bottom dock,
     which paints over it and swallows the tap). #wlBody has no bottom
     clearance of its own; --mx-chrome-bottom (mx_tokens.css:32) already
     computes dock-height + safe-area-inset-bottom and was defined but
     never consumed anywhere — this is its first real use rather than
     re-deriving the same calc. */
  #wlBody {
    padding-bottom: var(--mx-chrome-bottom, calc(64px + env(safe-area-inset-bottom, 0px)));
  }
}

/* Desktop: sheet host stays inert; no layout cost.
   Also reverse any mobile park/hide left by a phone→desktop resize. */
@media (min-width: 768px) {
  .mx-sheet {
    display: none !important;
  }
  .mx-app-sheet-trigger {
    display: none !important;
  }
  /* Floor tools must remain visible on desktop even if body was briefly flagged */
  body.mx-floor-app .bz-floor-tools.mx-floor-tools-parked,
  body.mx-floor-app .bz-floor-filters.mx-floor-filters-parked,
  .bz-floor-tools.mx-floor-tools-parked,
  .bz-floor-filters.mx-floor-filters-parked {
    display: flex !important;
    visibility: visible !important;
  }
  [data-mx-floor-sticky],
  .mx-floor-sticky {
    display: none !important;
  }
  /* Stall add bar never compact-hide siblings on desktop */
  body.mx-app-mobile #mbAddBar.mx-app-compact > .mx-container > :not(.mb-app-compact-row) {
    display: revert !important;
  }
  #view-browse,
  .stall-container,
  #bzMasthead,
  .bz-lcard {
    /* never apply mobile sheet geometry to desktop listing chrome */
    max-height: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   PART 1 MOBILE OVERHAUL — Drop / Floor / Card / Stall / Showcase (v6)
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
  /* ── Floor: hero feed first + 48px sticky search ─────────────────── */
  body.mx-floor-app .bz-floor-head.mx-floor-head-compact {
    padding-block: 8px 4px;
  }
  body.mx-floor-app .bz-floor-head.mx-floor-head-compact .bz-floor-lead {
    display: none;
  }
  body.mx-floor-app .bz-floor-head.mx-floor-head-compact .bz-floor-h2 {
    font-size: 18px !important;
    margin: 0;
  }
  body.mx-floor-app .bz-floor-tools.mx-floor-tools-parked,
  body.mx-floor-app .bz-floor-filters.mx-floor-filters-parked {
    display: none !important;
  }
  .mx-floor-sticky {
    position: sticky;
    top: var(--mx-sticky-top, var(--mx-nav-h, 48px));
    z-index: 45;
    height: auto;
    min-height: 48px;
    max-height: none;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    margin: 0 0 10px;
    padding: 6px 2px 8px;
    background: rgba(4, 5, 11, 0.92);
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  }
  .mx-floor-sticky__row {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
    min-height: 40px;
  }
  .mx-floor-quick {
    display: flex;
    flex-wrap: nowrap;
    gap: 6px;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .mx-floor-quick::-webkit-scrollbar {
    display: none;
  }
  .mx-floor-qchip {
    flex: none;
    min-height: 36px;
    min-width: 44px;
    padding: 0 12px;
    border-radius: 999px;
    border: 1px solid var(--mx-line2, #2a3150);
    background: var(--mx-panel, #0b0e1a);
    color: var(--mx-ink2, #aeb6d6);
    font: 650 12px Archivo, system-ui, sans-serif;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .mx-floor-qchip.is-on,
  .mx-floor-qchip[aria-pressed='true'] {
    color: var(--mx-void, #04050b);
    background: var(--mx-aurora-chip, var(--mx-mint, #7cffb2));
    border-color: transparent;
    font-weight: 700;
  }
  /* Listing CTAs sit in natural thumb zone of each card */
  body.mx-floor-app .bz-lcard-cta {
    position: relative;
    z-index: 2;
  }
  body.mx-floor-app .bz-lcard-buy,
  body.mx-floor-app .bz-lcard-offer {
    min-height: 44px !important;
  }
  body.mx-floor-app .bz-listing-cta .btn-primary {
    min-height: 48px;
    flex: 1 1 140px;
  }
  .mx-floor-search-sticky,
  #searchInput.mx-floor-search-sticky {
    flex: 1 1 auto !important;
    min-width: 0 !important;
    width: auto !important;
    height: 40px !important;
    min-height: 40px !important;
    margin: 0 !important;
    font-size: 16px !important;
  }
  .mx-floor-filter-btn,
  .mx-floor-file-btn {
    flex: none;
    min-height: 40px;
    min-width: 44px;
    padding: 0 12px;
    border-radius: 10px;
    border: 1px solid rgba(124, 255, 178, 0.4);
    background: rgba(124, 255, 178, 0.1);
    color: var(--mx-mint, #7cffb2);
    font: 700 12px Archivo, system-ui, sans-serif;
    letter-spacing: 0.02em;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .mx-floor-file-btn {
    border-color: rgba(255, 216, 124, 0.45);
    background: rgba(255, 216, 124, 0.12);
    color: var(--mx-gold, #ffd87c);
  }
  .mx-floor-filter-btn[aria-expanded='true'] {
    background: rgba(124, 255, 178, 0.22);
    box-shadow: 0 0 0 1px rgba(124, 255, 178, 0.25);
  }
  .mx-floor-filter-mount .bz-chip,
  .mx-floor-filter-mount select {
    width: 100%;
    max-width: 100%;
  }
  .mx-floor-filter-mount {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .mx-floor-filter-mount .bz-floor-tools {
    display: flex !important;
    flex-wrap: wrap;
    gap: 8px;
  }
  /* Listing cards: rich + primary CTAs */
  body.mx-floor-app .bz-lcard {
    border-radius: 14px !important;
  }
  body.mx-floor-app .bz-lcard .btn,
  body.mx-floor-app .bz-lcard [data-bzact='buy'],
  body.mx-floor-app .bz-lcard [data-bzact='offer'] {
    min-height: 44px;
  }

  /* ── Card image shell (detail modals) ────────────────────────────── */
  .mx-card-img-shell {
    position: relative;
    aspect-ratio: 2.5 / 3.5;
    width: min(78vw, 340px);
    border-radius: 14px;
    overflow: hidden;
    background: linear-gradient(160deg, #141a30, #0a0e18);
  }
  .mx-card-img-shell img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  #shopDetailLb .mx-card-img-shell,
  #pfInspect .mx-card-img-shell,
  .mk-lb .mx-card-img-shell {
    margin-inline: auto;
  }
  /* Prefer centered modal image on phone (not bottom-sheet squash for art) */
  #shopDetailLb.is-open,
  .mk-lb,
  .mb-card-lb {
    align-items: center !important;
    justify-content: center !important;
    padding: 16px !important;
  }
  #shopDetailLb .in {
    max-height: min(88dvh, 720px) !important;
  }

  /* ── Stall skin mobile aspect ────────────────────────────────────── */
  .stall-skin-bg,
  .mb-stall-band .mx-stall__art,
  #mbStallArt.stall-skin-bg,
  .mx-stall__art {
    width: 100%;
    background-size: cover !important;
    background-position: center !important;
    background-repeat: no-repeat !important;
    object-fit: cover;
    object-position: center;
  }
  .mb-stall-band.stall-banner,
  .mb-stall-band {
    min-height: 180px;
  }
  .mb-stall-band .mx-stall__art,
  #mbStallArt {
    min-height: 180px;
    height: 180px;
    border-radius: 12px;
  }
  /* Band art is absolute fill — constrain band height on phone */
  body.mx-app-mobile .mb-stall-band {
    min-height: 180px;
    max-height: none;
  }
  body.mx-app-mobile .mb-stall-band > .mx-stall__art {
    height: 100%;
    min-height: 180px;
    border-radius: 0;
  }

  /* ── Showcase / FavGacha player ──────────────────────────────────── */
  #mxFavGacha {
    z-index: 560 !important;
    align-items: center !important;
    justify-content: center !important;
    padding: max(12px, env(safe-area-inset-top)) 12px
      max(16px, env(safe-area-inset-bottom)) !important;
  }
  #mxFavGacha .fg-vp {
    width: 90vw !important;
    max-height: 70vh !important;
    aspect-ratio: 16 / 9 !important;
    margin: auto !important;
  }
  #mxFavGacha .fg-close {
    width: 44px !important;
    height: 44px !important;
    min-width: 44px !important;
    min-height: 44px !important;
  }
  /* Do not dock FavGacha as bottom sheet — keep centered dark viewport */
  #mxFavGacha[data-mx-sheet-ready],
  #mxFavGacha[data-mx-overlay] {
    align-items: center !important;
    justify-content: center !important;
    padding: max(12px, env(safe-area-inset-top)) 12px
      max(16px, env(safe-area-inset-bottom)) !important;
  }
  #mxFavGacha[data-mx-sheet-ready] > .fg-vp,
  #mxFavGacha[data-mx-overlay] > .fg-vp {
    width: 90vw !important;
    max-width: 90vw !important;
    max-height: 70vh !important;
    border-radius: 16px !important;
    margin: auto !important;
  }

  .mx-dock-pull.hot .mx-dock-pip {
    background: var(--mx-pink, #ff5c8a);
    box-shadow: 0 0 8px var(--mx-pink, #ff5c8a);
  }
}

@keyframes mxSkelShimmer {
  0% {
    background-position: 100% 0;
  }
  100% {
    background-position: -100% 0;
  }
}

/* Map sheet: mobile-only column layout (never force desktop map single-col) */
@media (max-width: 767.98px) {
  .mx-map-sheet,
  [data-mx-map-sheet] {
    display: flex !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    gap: 14px;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box;
    columns: auto !important;
    column-count: 1 !important;
  }
}

/* ── Hideout: landscape immersive + label purge (mobile) ───────────────── */
@media (max-width: 767.98px) {
  body.ho-land #mx-nav,
  body.ho-land .mx-dock,
  body.ho-land #avDock,
  body.ho-land #hoStickyRail,
  body.ho-land #hoThumbBar,
  body.ho-land #hoChips {
    display: none !important;
  }
  body.ho-land .ho-macro-label,
  body.ho-land .ho-zone-label,
  body.ho-land .ho-slot-tag,
  body.ho-land .ho-fix-label {
    display: none !important;
  }
  body.ho-land .ho-land-drawer {
    background: rgba(12, 12, 16, 0.78) !important;
    backdrop-filter: blur(12px) saturate(140%) !important;
    -webkit-backdrop-filter: blur(12px) saturate(140%) !important;
  }
  .ho-macro-label,
  .ho-zone-label,
  .ho-slot-tag {
    display: none !important;
  }
}

/* ═══ M1 NAV PHYSICS (Mobile App Mandate, 2026-08-09) ═══════════════════════
   Cross-document view transitions + persistent chrome. Scope is the DOCK's
   own band (<=760px, matching mx_tokens.css:424 and isPhoneChrome) plus
   pointer:coarse, so desktop and narrow non-touch windows see ZERO change.
   mx_shell.js carries the companion pagereveal/pageswap belt gate for any
   engine that hoists @view-transition out of its @media nest. */

/* Static dock shell: pages ship an EMPTY #avDock so the bar exists at first
   paint and survives document swaps as a named group. While empty it is
   invisible everywhere EXCEPT real phones, where it paints the reserved bar
   (61px measured hydrated height) — no pop-in, no reflow on hydrate. */
#avDock:empty { display: none !important; }
@media (max-width: 760px) and (pointer: coarse) {
  #avDock:empty {
    display: flex !important;
    /* !important: tokens' dock armor carries min-height:0 !important */
    min-height: 61px !important;
  }
  #avDock { view-transition-name: mx-dock; }
  #mx-nav { view-transition-name: mx-navbar; }
  /* Strip reserve: only surfaces that ALWAYS render a tape opt in via the
     .mx-strip-reserve class on their static #mx-strip-mount (40px measured);
     plain mounts stay zero-height so strip-less pages gain no dead band. */
  #mx-strip-mount.mx-strip-reserve { min-height: 40px; }
}

/* The opt-in. Both outgoing and incoming documents load this stylesheet
   render-blocking, so the rule is active on each side of every same-origin
   navigation. Engines without cross-document VT ignore this entirely and
   navigate exactly as before — pure progressive enhancement. */
@media (max-width: 760px) and (pointer: coarse) and (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}


/* === M4/M5 FEEL LAYER (Mobile App Mandate, 2026-08-09) ==================== */
@media (max-width: 767.98px) {
  /* F21: the wolf sigil (z 240) painted OVER open sheets (z 140), occluding
     tappable rows - hide it whenever a sheet owns the screen */
  body.mx-sheet-open #mxSigil { opacity: 0 !important; pointer-events: none !important; }

  /* F45/F18: pressed feedback must be INSTANT down, soft up. The 200ms
     transition rode both directions, so a tap showed nothing until release. */
  .mx-dock-item:active,
  .mx-dock-item.is-pressed,
  .mb-lrow:active,
  .mb-cta:active,
  .mx-btn-secondary:active,
  .mb-status:active,
  .chip:active {
    transition-duration: 0.04s !important;
  }
  .mx-dock-item.is-pressed {
    color: var(--mx-ink);
    background: rgba(124, 255, 178, 0.1);
  }

  /* F19/F44: sheet exit - down-slide + scrim fade instead of a display pop */
  .mx-sheet.is-closing { display: flex !important; }
  .mx-sheet.is-closing .mx-sheet__panel {
    animation: mx-sheet-down 0.22s cubic-bezier(0.4, 0, 1, 1) both;
  }
  .mx-sheet.is-closing { animation: mx-scrim-out 0.22s ease-out both; }
  .mx-sheet.is-open { animation: mx-scrim-in 0.2s ease-out; }
  @keyframes mx-sheet-down {
    /* P2-5: drag-to-dismiss releases resume the exit from the finger's
       translate (JS sets --mx-sheet-drag-y on the panel right before
       closeSheet). Every non-drag close leaves the var unset → 0 → the
       exact pre-P2-5 exit. */
    from { transform: translateY(var(--mx-sheet-drag-y, 0px)); }
    to { transform: translateY(103%); }
  }
  @keyframes mx-scrim-in { from { background-color: transparent; } }
  @keyframes mx-scrim-out { to { background-color: transparent; opacity: 0; } }

  /* P2-5 (F20): sheet drag-to-dismiss support.
     1) Grip/head own vertical pans: pan-x means the browser never claims a
        vertical gesture starting there for panel scroll (which would
        pointercancel the drag). The panel/body keep their pan-y +
        overscroll containment — touch-action:none stays banned (#mxSigil
        is the only exception, unchanged).
     2) The 40x4 grip is a promise, not a touch target — grow its hit box.
     3) mx-drag-touched (JS adds it on the first engaged drag, openSheet
        clears it on every open): the is-open entrance animation holds its
        fill at translateY(0) forever, which would override the inline drag
        translate. Releasing the animation only AFTER a real drag keeps the
        entrance slide-up for every open. */
  .mx-sheet__grip,
  .mx-sheet__head,
  .mx-account-sheet__grip,
  .mx-account-sheet__h {
    touch-action: pan-x;
  }
  .mx-sheet__grip,
  .mx-account-sheet__grip {
    position: relative;
  }
  .mx-sheet__grip::before,
  .mx-account-sheet__grip::before {
    content: '';
    position: absolute;
    inset: -12px -44px;
  }
  .mx-sheet.mx-drag-touched.is-open .mx-sheet__panel {
    animation: none;
  }

  /* F46: the 360ms section feed-in replays on every MPA hop - suppress on
     back/forward (mx_shell stamps the class), keep it for cold entries */
  html.mx-no-feedin main > section,
  html.mx-no-feedin main .mx-container > section,
  html.mx-no-feedin .pf-wrap > section {
    animation: none !important;
  }

  @media (prefers-reduced-motion: reduce) {
    .mx-sheet.is-closing .mx-sheet__panel,
    .mx-sheet.is-closing,
    .mx-sheet.is-open { animation: none !important; }
  }
}

/* ═══ P2-7 BACK CHIP (F10) ═══════════════════════════════════════════════
   Deep-page escape hatch for standalone/PWA (no browser back button).
   mx_shell.js ensureBackChip() only injects #mxNavBack on phone chrome
   (isPhoneChrome && isTouchDevice) on non-dock pages; this is the CSS belt:
   outside the dock's own band the chip renders NOWHERE, even if a stale
   DOM node survives a resize race. Geometry mirrors .mx-nav-account-btn
   (44px square, 10px radius, panel2 fill); order 0 seats it before the
   logo (order 1) at the head of the strip. */
.mx-nav-back { display: none !important; }
@media (max-width: 760px) and (pointer: coarse) {
  .mx-nav-back {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    flex: none;
    order: 0;
    width: 44px;
    min-width: 44px;
    height: 44px;
    min-height: 44px;
    padding: 0;
    margin: 0;
    border-radius: 10px;
    border: 1px solid var(--mx-line2);
    background: var(--mx-panel2);
    color: var(--mx-ink2);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .mx-nav-back:active {
    color: var(--mx-ink);
    border-color: var(--mx-ink3);
    transition-duration: 0.04s;
  }
}
