/* ==========================================================================
   Air Market - v9 mobile layer, topbar collapse (V9-MOBILE-SPEC.md
   section 3.1). Owner: T1. Paired JS: assets/js/v3/topnav.js (menu sheet,
   search row, focus trap, Escape, scroll lock, ARIA wiring) and the Stack
   pill dock.

   At <= 720px the two-tier bar becomes a single 56px row: brand mark only
   (the wordmark already dies at 860px, store-v3-topnav.css section 4),
   three 44px icon buttons on the right (search, Saved with its count dot,
   menu), tier 1 (trust strip, utility links, UI switch) hidden entirely
   since its links already live in the footer's Quick Links. The menu opens
   a full-width sheet reusing the existing mega-panel slot
   (data-v3-slot="mega") in a mobile presentation; search opens a
   full-width row backed by the existing #v3Search handlers; the Stack
   trigger moves out of the bar to a docked floating pill.

   THIS is the sheet that releases the shell's 826px floor
   (V9-MOBILE-SPEC.md section 1.1; store-v9-mobile.css measures and
   documents the floor but deliberately adds no rule for it - that
   measurement is the starting point for this file, not a fix already
   applied). #topbar's .v3-tier2-in nowrap row is what forces the 826px
   minimum onto .window; collapsing it into the mobile bar described above
   is what makes .window's scrollWidth finally equal its clientWidth at
   <= 720px.

   HARD RULE, identical across all seven v9 sheets: every declaration in
   this file must live inside `@media (max-width: 720px)` or
   `@media (max-width: 480px)` (store-v9-mobile.css's BREAKPOINTS section
   explains why those two numbers and not a ladder). A rule outside either
   query is a desktop regression by definition - the >= 861px byte-identity
   bar this whole layer exists to hold depends on that being true of every
   one of the seven sheets, not just six of them.

   SCOPING: mirror the specificity of whatever you are overriding - see
   store-v9-mobile.css's SCOPING section for the exact prefixes.
   #topbar and its tiers are v3/v4/v5-era:
   `:root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"])`. A brand-new
   class this sheet introduces (the mobile menu sheet, the search row, the
   docked Stack pill are all new surfaces) needs no prefix - there is
   nothing above to tie-break against. Never !important.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. HOW THE FOUR AFFORDANCES ARE BUILT, so the next reader does not have to
   diff two files to find out.

   Nothing in this sheet duplicates a control. The bar's existing elements are
   reused in every case, because a second search input, a second Sign in or a
   second account link is a second thing to keep in sync with a session, a
   count and a route - and the first one to drift is the one nobody is looking
   at. Concretely:

     BRAND      the existing .v3-brand, mark only. The wordmark and its rule
                are already display:none from 860px down (store-v3-topnav.css
                section 4), so this sheet only takes the lockup's right margin
                off and gives it a 44px box.
     SEARCH     the existing .v3-search box and the existing #v3Search input,
                with all of topnav.js's handlers (Enter to search, Escape to
                clear, the Cmd-K binding) untouched. It is display:none in the
                bar row at <= 720px and revealed, full width, only while
                :root carries [data-v9-search="open"] - which topnav.js sets
                from the new search icon button. There is no second field.
     SAVED      the existing .v3-saved anchor, grown from 32px to 44px, count
                dot nudged inside the larger box. Its accessible name still
                comes from syncSaved() and the dot is still aria-hidden, so
                the count is announced exactly once, exactly as on desktop.
     MENU       the existing mega panel ([data-v3-slot="mega"], id v3MegaPanel)
                re-presented as a stacked sheet, with the real nav links, the
                real Sign in button and the real account anchor MOVED into it
                by topnav.js at <= 720px (and moved back above 720px). Moved,
                not copied: is-current, aria-current, the Sign in label that
                follows the session, and the Updates count all keep working
                because they are still the same three elements topnav.js's
                update() writes to.
     STACK      the existing [data-v3-slot="stack"] and stack.js's own button,
                position:fixed to the bottom-right corner. No JS, no second
                trigger, and the slot leaves the bar's flex row entirely -
                which is part of how the 802px floor goes away.

   THE FLOOR, AND WHY THIS IS THE SHEET THAT REMOVES IT. #topbar's tier-2 row
   is flex-wrap:nowrap with six children that cannot shrink, so its min-content
   width becomes .window's floor: measured on this tree at 390x844 and 320x800,
   `div.window` reported scrollWidth 802 / clientWidth 390 and 802 / 320 on
   every one of the routes probed, with the widest-child walk naming
   .v3-row.v3-tier2-in every time. The hidden set below (.v3-nav, .v3-search,
   .v3-account, and since the session control moved into this row, .v3-signin)
   takes those children out, one more (.v3-stack-slot) is
   taken out of flow, and what is left is a brand mark plus three 44px buttons
   - about 190px of min-content at 320px. Nothing else in this sheet is load
   bearing for AC1; everything else is the cost of making the result usable.

   WHY HIDING TIER 1 IS SAFE, not just convenient. Tier 1 carries the trust
   strip, two utility links (Creators, Publish), and the design switch slot
   (Sign in lives in tier 2's utility zone now, precisely so no collapse can
   take it). Taking each in turn: the trust strip is already suppressed by
   assets/js/flags.js in this build and renders as .v3-trust--empty; the design
   switch is no longer rendered at all (V8-BUILD-PLAN.md 0.A.1) and its slot is
   empty; and Creators / Publish are the two
   links store-v3-topnav.css's own 940px step already drops, on the stated
   grounds that "the tier-1 links all live in the footer's Quick Links too"
   (that file, section 4). So this sheet drops nothing that the page below it
   does not still offer, which is the only test that matters - a mobile bar
   that hides a destination with no second path to it is not a collapse, it is
   a deletion.

   NO 480px RULES IN THIS FILE, deliberately. store-v9-mobile.css offers two
   breakpoints and this sheet needs one: measured at 320px, the collapsed row
   spends about 190px of its 260px content box, so there is nothing left for a
   second step to fix. A rule at 480px that changes nothing observable is a
   rule the next reader has to prove is dead.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   1. ONE 56px ROW, AND is-condensed AS A NO-OP.

   The two tier heights are custom properties declared once on
   :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) by
   store-v3-core.css section 1, and #topbar.is-condensed swaps each for its
   *-condensed twin (that file, section 5). Redefining all four here - same
   selector, same specificity, later sheet, so this wins the tie-break - does
   two jobs with four declarations:

     1. the bar is one 56px row at every scroll position;
     2. condense-on-scroll becomes a genuine no-op instead of a suppressed
        one, because .is-condensed still resolves --v3-bar-h through
        --v3-bar-h-condensed and both now read 56px. Nothing needs to know
        the class is inert - core.js keeps toggling it, "shell:condense"
        keeps firing, and the height it would have animated to is the height
        the bar is already at. That is the requirement in spec 3.1's last
        bullet, and it is cheaper and harder to break than a rule per tier.

   --v3-bar-total (tier 1 + tier 2) falls out of this at 56px, which is what
   the other v9 sheets offset sticky chrome against, and what the menu sheet
   below measures its own height from.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) {
    --v3-tier1-h: 0px;
    --v3-tier1-h-condensed: 0px;
    --v3-bar-h: 56px;
    --v3-bar-h-condensed: 56px;
  }

  /* Tier 1 goes entirely - see section 0 for why each of its four passengers
     is either already gone, moved, or reachable from the footer. display:none
     rather than height:0 because height:0 + overflow:hidden leaves the links
     focusable, which is the trap store-v3-topnav.css section 2 documents
     against its own condensed state. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier1 { display: none; }

  /* The row itself. The gutter is already 16px here (store-v9-mobile.css's
     step-down, which .v3-row reads through var(--v3-gutter)), so the only
     thing missing is a gap: on desktop the spacing between zones comes from
     .v3-brand's 32/16px right margin and .v3-search's margin-left:auto, and
     with the search box out of the row both of those stop describing
     anything. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier2-in {
    gap: var(--space-8);
  }

  /* Mark only, and a 44px box for the thumb - the lockup is the home link and
     is one of the few controls on this row a reader will hit by muscle memory
     rather than by looking. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-brand {
    height: 44px;
    margin-right: 0;
  }

  /* The four children the collapsed row does not carry. Every one of these
     selectors is scoped to .v3-tier2-in rather than to #topbar, which is what
     lets the same elements be visible once topnav.js has moved them into the
     menu sheet: the sheet is not inside .v3-tier2-in, so none of these rules
     reach it and nothing has to be un-hidden on the other side. .v3-search is
     the exception that comes back, in section 4, when search opens. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier2-in .v3-nav,
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier2-in .v3-search,
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier2-in .v3-account,
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier2-in .v3-signin {
    display: none;
  }

  /* margin-left:auto moves from the search box (gone) to the utility zone, so
     the three icon buttons still collect against the right edge. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-utility {
    gap: var(--space-4);
    margin-left: auto;
  }
}

/* --------------------------------------------------------------------------
   2. THE THREE ICON BUTTONS: search, Saved, menu.

   44px is the Apple HIG floor spec 3.6 adopts, and it is the size of the box,
   not of the glyph: the icons stay at 20px so the row reads as chrome rather
   than as a toolbar of oversized symbols. .v9-bar-btn is new to this layer,
   so per store-v9-mobile.css's SCOPING it needs no [data-ui] prefix to win
   anything - it is prefixed anyway, because these buttons only ever exist
   inside #topbar and a bar rule that does not look like the other bar rules
   is the one someone deletes by accident.

   Both new buttons are rendered with the `hidden` attribute and un-hidden by
   topnav.js at <= 720px (and re-hidden above it). That is deliberate: it
   keeps the DOM's own answer to "is this control available" honest at both
   widths instead of leaving an author display: declaration to beat the UA's
   [hidden]{display:none}, which is the trap store-v3-topnav.css:481 already
   pays for once. It also means the desktop bar carries two display:none
   children that contribute nothing to the nowrap row's min-content width and
   nothing to .v3-utility's flex gaps.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-bar-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--color-text-secondary);
    transition: background-color var(--v3-ease), color var(--v3-ease);
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-bar-btn .ic { --ic-size: 20px; }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-bar-btn:hover {
    background: var(--color-action-secondary);
    color: var(--color-text-primary);
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-bar-btn:focus-visible {
    outline: 2px solid var(--color-border-focus);
    outline-offset: 2px;
  }
  /* The menu button draws its own aria-expanded, the same way the Browse
     trigger does (store-v3-topnav.css section 3b), so the state cannot be
     announced without also being visible. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-bar-btn[aria-expanded="true"] {
    background: var(--color-action-secondary);
    color: var(--v3-ink);
  }

  /* Saved keeps its glyph, its count dot and its aria-label; only the box
     grows. The dot moves INSIDE the larger box (it hangs 2-3px outside a 32px
     one, which at 44px with a 4px gap would sit on the menu button's edge). */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-saved {
    width: 44px;
    height: 44px;
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-saved .ic { --ic-size: 20px; }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-saved-n {
    top: 4px;
    right: 4px;
  }
}

/* --------------------------------------------------------------------------
   3. THE MENU SHEET.

   The panel is [data-v3-slot="mega"] itself, not a new surface: mega.js has
   already built the taxonomy into it by the time the menu button can be
   pressed, and topnav.js prepends one block (.v9-menu) carrying the moved nav
   links, Sign in and the account entry. So the sheet is "the Browse panel,
   stacked, with the bar's own destinations on top".

   Its geometry is inherited, not re-specified: store-v3-mega.css already
   pins the panel position:absolute; top:100%; left:0; right:0 under #topbar
   and caps it at the viewport minus the bar, which at these widths IS a
   full-width sheet under a 56px bar. This sheet only restacks the inside,
   and reaches the bottom edge by dropping the 16px of breathing room that cap
   leaves - on a phone that gap is a 16px stripe of page peeking under an
   otherwise full-height sheet, which reads as a rendering fault.

   Because the geometry is not state-dependent, none of these rules need a
   [data-v9-menu="open"] guard: the panel carries `hidden` when closed, and
   store-v3-mega.css re-asserts display:none for it at a specificity that
   beats its own display:block. Only the scroll lock below needs the state,
   because the element it acts on (.content) is not inside the panel.

   ONE THING IS DROPPED HERE and it is worth saying out loud: the "Newly
   checked" featured column. It is a merchandised 16:10 tile, not a
   destination list, and at 390px it costs roughly 250px of vertical scroll
   between the taxonomy and the panel's own footer rows. Spec 3.1 asks the
   sheet to carry "the mega panel's lists"; this is the one child of the panel
   that is not a list.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar [data-v3-slot="mega"] {
    max-height: calc(100vh - var(--v3-bar-total) - var(--v3-filterbar-h));
    border-bottom: 0;
    box-shadow: var(--shadow-lg);
    /* The sheet is its own scroller; a flick that reaches its end must not
       hand the rest of the gesture to .content behind it. */
    overscroll-behavior: contain;
  }

  /* Body/content scroll lock. .content is the scroll container under this
     shell, not <body> (store-v3-core.css section 3), so locking scroll means
     locking .content - and the state has to live on :root because .content is
     the panel's sibling's sibling, not its descendant. */
  :root[data-v9-menu="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .content {
    overflow: hidden;
  }

  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-mega-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-24);
    padding: var(--space-16) var(--v3-gutter) var(--space-24);
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-mega-col--feat { display: none; }

  /* Rows: centred rather than top-aligned once they are 44px tall, because a
     one-line row with align-items:start reads as mis-set rather than as
     deliberate. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-mega-row {
    align-items: center;
    min-height: 44px;
    padding: var(--space-8) var(--space-12);
  }

  /* The panel's footer row is two links ("Everything in the store", "All
     categories") laid out inline. At 320px they wrap into two ragged lines
     anyway, so they become two full-width rows like everything above them. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .v3-mega-list--inline {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--space-8) calc(var(--v3-gutter) - var(--space-12));
  }

  /* ---- 3a. The moved block ---- */

  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu {
    padding: var(--space-8) var(--v3-gutter) var(--space-12);
    border-bottom: 1px solid var(--v3-line);
  }

  /* The catalogue nav, vertical. .v3-nav's own display:flex and 4px gap come
     from store-v3-topnav.css and still apply in here; only the axis changes. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu .v3-nav {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }
  /* Browse is a disclosure for the taxonomy that is already open below it, so
     inside the sheet the trigger has nothing left to disclose. It stays in the
     DOM (mega.js owns it and reads it back through
     [data-v3-mega-toggle]) and display:none keeps it out of the tab order and
     out of the focus trap's list. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu .v3-browse { display: none; }

  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu .v3-link {
    height: 48px;
    padding: 0 var(--space-12);
    font-size: 15px;
    border-radius: var(--radius-md);
  }
  /* The current-section marker is a 2px rule under a 36px pill on desktop.
     Under a full-width row that would read as a divider, so it becomes a bar
     down the leading edge - same two channels (darker label plus a mark), one
     that survives the shape change. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu .v3-link.is-current::after {
    left: 0;
    right: auto;
    top: var(--space-8);
    bottom: var(--space-8);
    width: 3px;
    height: auto;
  }

  /* Sign in and the account entry, under a rule. Both are the real controls:
     the button is still the one bindSignIn() reflects the session onto, and
     the anchor is still the one syncUpdates() writes the count to. */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu-acct {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-top: var(--space-8);
    padding-top: var(--space-8);
    border-top: 1px solid var(--v3-line);
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu-acct .v3-account {
    justify-content: flex-start;
    gap: var(--space-12);
    min-height: 48px;
    padding: var(--space-4) var(--space-12);
    border-radius: var(--radius-md);
  }
  /* The avatar alone is not a destination anyone can name, so topnav.js gives
     the anchor a visible label (aria-hidden, because the anchor's own
     aria-label already says "Your account" plus the count - two announcements
     of the same thing is worse than none). */
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu-acct .v9-acct-lbl {
    font-size: 15px;
    font-weight: 600;
    color: var(--v3-ink);
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-menu-acct .v3-util-link {
    justify-content: flex-start;
    height: 48px;
    padding: 0 var(--space-12);
    border-radius: var(--radius-md);
    font-size: 15px;
    font-weight: 600;
    color: var(--v3-ink);
  }
}

/* --------------------------------------------------------------------------
   4. THE SEARCH ROW.

   One field in the whole shell, revealed rather than rebuilt. The row is the
   same .v3-tier2-in flex line; while :root carries [data-v9-search="open"]
   the brand and the icon buttons stand down and the real .v3-search box takes
   the width, with a text Cancel beside it. Everything downstream of the input
   - Enter to search via submitSearch(), Escape to clear, the Cmd-K focus
   binding, and update()'s reflection of a #/search/<q> route back into the
   field - is topnav.js's existing wiring, untouched.

   Cancel does NOT clear the query. Escape does (that is the field's own
   documented behaviour, store-v3-topnav's paired JS), but Cancel means "put
   the bar back", and on a #/search/<q> route the value in that field is the
   query the page below is showing - clearing it as a side effect of closing a
   row would make the bar disagree with the results under it.

   The 16px input is not decoration: iOS Safari zooms the viewport on focus
   for any input under 16px, and a zoom that fires the moment search opens is
   indistinguishable from the layout breaking. Spec 3.6 sets the same floor
   for body text; this one sits here because the field is this sheet's.

   The docked Stack pill is deliberately NOT hidden with the rest of the
   utility zone: it is over the page, not in the bar, and having it blink out
   because a text field opened would read as a bug in the page rather than as
   part of the mode change. Hence the two explicit selectors below instead of
   one on .v3-utility, which would have taken the fixed slot with it.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root[data-v9-search="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-brand,
  :root[data-v9-search="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-utility .v9-bar-btn,
  :root[data-v9-search="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-utility .v3-saved {
    display: none;
  }

  :root[data-v9-search="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-tier2-in .v3-search {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
    height: 40px;
    margin-left: 0;
  }

  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-search-input {
    font-size: 16px;
  }

  /* Guarded on the open state, and that guard is load bearing rather than
     tidiness: `display` on a [hidden] element is an AUTHOR declaration and
     beats the UA's [hidden]{display:none} at any specificity (the trap
     store-v3-topnav.css:481 already pays for once). Written unguarded, this
     rule put a live Cancel button next to the brand on every mobile route -
     measured, screenshotted, and the reason the selector reads like this. */
  :root[data-v9-search="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-search-cancel {
    display: inline-flex;
    align-items: center;
    flex: none;
    height: 44px;
    padding: 0 var(--space-4);
    border: 0;
    background: transparent;
    font-family: var(--font);
    font-size: 15px;
    font-weight: 600;
    line-height: 1;
    color: var(--v3-ink);
    white-space: nowrap;
  }
  :root[data-v9-search="open"]:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-search-cancel:focus-visible {
    outline: 2px solid var(--color-border-focus);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }
}

/* --------------------------------------------------------------------------
   5. THE DOCKED STACK PILL.

   position:fixed on the SLOT, not on stack.js's button: the slot is this
   file's to place (store-v3-topnav.css section 3d styles it and says
   "stack.js owns everything inside; the slot only has to hold its place"),
   and moving the slot means stack.js keeps writing into the same element it
   always did, keeps its count, its teal all-checked shield and its
   aria-expanded, and needs no mobile branch at all.

   Two consequences worth stating. First, taking the slot out of flow is part
   of the AC1 fix, not just a relocation - a nowrap row cannot be floored by a
   child that is not in it. Second, the pill needs no z-index: it stays inside
   #topbar's stacking context (--v3-z-bar: 60), which puts it over .content
   for free, UNDER the mega panel (--v3-z-mega: 58 beats a positioned
   z-index:auto sibling) so an open menu sheet covers it rather than fighting
   it, and under Air_DS dialogs at 100 - including the Stack's own drawer,
   which is the one overlay this button must never paint on top of.

   The button grows to 48px, not 44: it is the only floating control on the
   page, it sits where a thumb rests rather than where the eye is, and it is
   the affordance REDESIGN section 3 calls the store's signature mechanic. The
   shadow is what separates it from whatever card it happens to be over.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-stack-slot {
    position: fixed;
    right: var(--space-16);
    bottom: var(--space-16);
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-stack-slot .v3-stack-btn {
    height: 48px;
    padding: 0 var(--space-16);
    border-color: var(--color-border-default);
    box-shadow: var(--shadow-md);
    transition: background var(--v3-ease), box-shadow var(--v3-ease);
  }
}

/* --------------------------------------------------------------------------
   6. TIER 3, THE ONE SURFACE THIS SHEET REACHES OUTSIDE THE COLLAPSE.

   The filter bar ([data-v3-slot="filterbar"], store-v3-filters.css) is the
   bar's third tier and therefore this task's, not a page surface. Its row
   already wraps, so it cannot floor .window the way tier 2 did - but its chip
   strip is a Tabs component whose own row does not wrap, so on a catalogue
   route at 320px the chips would be the next thing to overflow once tier 2
   stops being the widest child. It becomes a scroll rail, which is what a chip
   strip is on a phone; store-v9-mobile.css's v9-rail primitive documents the
   four declarations, and this is case 2 in it (an existing container that
   cannot be given a class from CSS).
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-fb-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-fb-chips::-webkit-scrollbar { display: none; }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-fb-chips .tab { flex: none; }
}

/* --------------------------------------------------------------------------
   7. MOTION.

   Two transitions are added by this sheet - the icon buttons' hover fade and
   the docked pill's shadow - and both are switched off here, in the same
   spirit as store-v3-topnav.css section 5. The menu sheet's entrance is the
   panel's existing v3MegaIn animation, which store-v3-mega.css section 9
   already disables under reduced motion, so it is deliberately not repeated.

   The query is `and`-joined rather than nested so that every rule in this
   file still sits inside a max-width query, which is the structural promise
   the seven v9 sheets make together (store-v9-mobile.css, BYTE-IDENTITY).
   -------------------------------------------------------------------------- */

@media (max-width: 720px) and (prefers-reduced-motion: reduce) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v9-bar-btn,
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) #topbar .v3-stack-slot .v3-stack-btn {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   8. THE FOOTER'S RIGHT-HAND MARK (store.css:1133, unscoped).

   `.foot-bottom` is a `space-between` row - the certification badges on the
   left, `.foot-copy` on the right - and it wraps (`flex-wrap: wrap`,
   store.css:1125), so on a phone the two land in a stack. `margin-left: auto`
   would then leave the second row flush right under a left-aligned badge row,
   which reads as a layout fault rather than a deliberate edge. Neutralising it
   starts the mark at the same gutter the badges above it do.

   This rule used to also set `text-align: left`, because .foot-copy was a 54ch
   paragraph of right-aligned prose. That sentence is gone (app.js storeFooter,
   which says why) and what is left is the AIR wordmark alone, so there is no
   text to align and only the margin still matters.

   This sheet rather than a new one: the footer is shell furniture, the same
   thing this file owns at the top of the page, and its own header already
   depends on what the footer does and does not carry.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  .foot-copy {
    margin-left: 0;
  }
}

/* --------------------------------------------------------------------------
   9. THE TOAST (store.css:1778, unscoped).

   Reported from a real iPhone and a real Android as "not visible". Three
   things are wrong with it on a phone, and only the first is arguable:

   1. IT IS ANCHORED INTO THE BROWSER'S OWN FURNITURE. `bottom: 26px` is
      measured off the layout viewport, and a phone browser parks a toolbar,
      a gesture bar or a home indicator in exactly that band. There is no
      safe-area inset anywhere in the declaration, so on a notched device the
      last 34px of it are under the indicator before the toolbar is counted.
   2. IT LANDS UNDER THE DOCKED STACK PILL. Section 5 above puts a 48px
      floating pill at `bottom: var(--space-16)` on every mobile route - the
      one thing on the page that is guaranteed to be in the bottom band the
      toast centres itself into. The pill is inside #topbar's stacking
      context and the toast is a body child at the top of the z scale, so it wins
      the paint - which means the message a reader has 10 seconds to notice
      arrives on top of the store's signature control.
   3. IT IS A SLIVER. `left: 50%` + `translate(-50%)` on a shrink-to-fit flex
      box with a 520px max-width gives a 188px column at 375px: "Saved on
      this device. Sign in to keep your list on your phone and desktop."
      wraps to four lines in a box narrower than the two buttons above it.

   So: a full-width band inside the gutter, lifted clear of the pill
   (16 + 48 + 16) and of whatever the device reserves below that
   (`env(safe-area-inset-bottom)`, which is 0 on hardware that reserves
   nothing, hence the fallback rather than a second rule). The entrance
   transform is restated because the base sheet's `.in` rule carries the
   -50% centring inside it; same specificity, later sheet, so this is the
   one that lands.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  .v4-toast {
    left: var(--space-16);
    right: var(--space-16);
    bottom: calc(env(safe-area-inset-bottom, 0px) + 80px);
    max-width: none;
    transform: translate(0, 12px);
  }
  .v4-toast.in {
    transform: translate(0, 0);
  }
}
