/* mobile.css
   The dedicated mobile interface: app bar, bottom tab bar, bottom sheets,
   page transitions, and the phone-sized layout for every student page.

   HOW THIS FILE IS SCOPED, AND WHY IT MATTERS
   -------------------------------------------
   Desktop must keep the existing design untouched, so every rule below is
   written under `html.is-mobile` and simply does not match on a laptop, a TV
   or a wide browser window. The class is added before first paint by the
   inline snippet at the top of each page's <head> — inline rather than an
   external file because a network round trip before paint is exactly long
   enough to show the desktop layout and then swap it.

   The only unscoped rules are the ones directly below, which hide the mobile
   chrome. That markup ships in every page's HTML so the tab bar can paint on
   the first frame instead of being injected by script, which means desktop
   would otherwise render a bare unstyled nav.

   Every custom property is written with a fallback — `var(--primary, #e07b3c)`
   — because the admin pages link this file but deliberately do not link
   shared.css, so there is no guarantee the token has been declared. Same
   convention, and the same reason, as loader.css.

   STICKY, NOT FIXED
   -----------------
   Only the tab bar is `position: fixed`, and it is a direct child of <body>.
   Every other piece of chrome — app bars, the order screen's action bar — is
   `position: sticky` inside the page wrapper. That is not a style preference:
   the page wrapper is translated during the transition at the bottom of this
   file, and an ancestor with a transform becomes the containing block for its
   fixed descendants, so a fixed app bar would detach and jump on every
   navigation. Sticky is unaffected, and page chrome sliding with its own page
   is the behaviour we want anyway. The tab bar is global, so it stays outside
   the transform and never moves.
*/

/* ---------- Views (home.html only) ----------
   home.html holds four screens — Menu, Orders, Order, Profile — that used to
   be four separate documents. Exactly one is ever on screen, on every device:
   unlike the admin dashboard's data-m-section cards (mobile-only tabs; every
   card stays visible at once on desktop, because that page was always meant
   to read as one long scroll there), these four were four genuinely distinct
   full-screen designs even on desktop, and merging the documents was never
   meant to stack Today's Menu, Your Orders and My Profile on top of each
   other. So this rule is unscoped — it is exactly as true on a laptop as on a
   phone — and home.html's own script is what decides which one has
   `.is-active`, through showView() / window.KMCTShowView. */
.m-view { display: none; }
.m-view.is-active {
    display: block;
    /* A small settle rather than a hard cut, on entry only — matching the
       cross-document fade every other navigation in the app already has.
       Reduced motion collapses this to 0.001ms via the global rule in
       shared.css, same as everything else here. */
    animation: m-view-in 220ms ease both;
}
@keyframes m-view-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ---------- Mobile-only chrome, inert everywhere else ---------- */
/* Present in the markup of every page; revealed only by the rules further
   down. Without this, a desktop visitor gets an unstyled list of nav links. */
.m-appbar,
.m-tabbar,
.m-only { display: none; }

html.is-mobile .m-appbar { display: flex; }
/* Scoped to the same class that already drives the tab bar's bottom-clearance
   padding on .m-page (below) and on the route loader — every page that carries
   a tab bar in its markup already sets this on <body>, so this changes nothing
   for any of them. What it buys is a single flag home.html's script can toggle
   at runtime: its Order view is a full-screen pushed screen with no tab bar of
   its own (never did, back when it was its own document), and switching this
   one class off for that view removes the bar and the space reserved for it
   together, instead of needing two separately-tracked states. */
html.is-mobile body.m-has-tabs .m-tabbar { display: flex; }
html.is-mobile .m-only { display: block; }
/* The mirror image: parts of the desktop design that have no place in the app
   layout, chiefly header links the tab bar now carries. */
html.is-mobile .m-hide { display: none !important; }

/* ================================================================
   Tokens and base
   ================================================================ */
html.is-mobile {
    --m-tabbar-h: 60px;
    --m-appbar-h: 56px;
    --m-safe-t: env(safe-area-inset-top, 0px);
    --m-safe-b: env(safe-area-inset-bottom, 0px);
    --m-gutter: 16px;
    /* iOS-style deceleration. Sharper than the shared --transition, which is
       tuned for hover states rather than things that slide across a screen. */
    --m-ease: cubic-bezier(0.32, 0.72, 0, 1);
    --m-card-r: 18px;
    --m-tint: #fdf3e7;
    -webkit-text-size-adjust: 100%;
}

html.is-mobile body {
    /* Every student page sets `body { padding: 20px }` (and order/profile also
       centre themselves with flex) in their own <style>. Those blocks come
       after this file in the <head>, so this wins on specificity rather than
       on order — no !important needed. */
    display: block;
    padding: 0;
    /* dvh, not vh: the mobile URL bar collapsing on scroll changes vh and the
       whole layout jumps. shared.css keeps its 100vh so the desktop cascade is
       untouched. */
    min-height: 100dvh;
    background: var(--bg, #f3f4f6);
    -webkit-tap-highlight-color: transparent;
    overscroll-behavior-y: none;
}

html.is-mobile button,
html.is-mobile a,
html.is-mobile input,
html.is-mobile select { touch-action: manipulation; }

/* iOS zooms the page in when a focused input's text is under 16px, and every
   form control on the site is 0.95rem. */
html.is-mobile input,
html.is-mobile select,
html.is-mobile textarea { font-size: 16px; }

/* ================================================================
   Page shell
   ================================================================ */
html.is-mobile .m-page {
    max-width: none;
    width: auto;
    margin: 0;
    padding: 0 var(--m-gutter) 24px;
    min-height: 100dvh;
    /* Paired with the transform states at the bottom of this file. The opacity
       half is already supplied by shared.css's `body > *` rule. */
    transition: transform 300ms var(--m-ease), opacity 0.28s ease;
}

/* Set on <body> by pages that carry a tab bar. One class drives the bottom
   padding here and the route loader's inset below. */
html.is-mobile body.m-has-tabs .m-page {
    padding-bottom: calc(var(--m-tabbar-h) + var(--m-safe-b) + 24px);
}
/* The auth screens fill the viewport and centre their own content. */
html.is-mobile body.m-auth .m-page {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-top: calc(var(--m-safe-t) + 28px);
    padding-bottom: calc(var(--m-safe-b) + 28px);
}

/* Chrome that has to reach both screen edges while sitting inside a gutter. */
html.is-mobile .m-bleed {
    margin-inline: calc(-1 * var(--m-gutter));
    padding-inline: var(--m-gutter);
}

/* ================================================================
   App bar
   ================================================================ */
html.is-mobile .m-appbar,
html.is-mobile .home-container > .header,
html.is-mobile .orders-wrapper > .header {
    position: sticky;
    top: 0;
    z-index: 900;
    display: flex;
    /* Explicit, not inherited: home.html and orders.html each carry a
       `@media (max-width: 600px)` block that stands this header up as a
       column, which is exactly the width this file is running at. */
    flex-direction: row;
    align-items: center;
    gap: 10px;
    min-height: calc(var(--m-appbar-h) + var(--m-safe-t));
    margin: 0 calc(-1 * var(--m-gutter)) 14px;
    padding: var(--m-safe-t) var(--m-gutter) 0;
    background: var(--card, #fff);
    border: none;
    border-bottom: 1px solid var(--border, #e2e8f0);
    border-radius: 0;
    /* Flat rather than shadowed: content scrolls right up under it, and a
       shadow reads as a seam on a small screen. */
    box-shadow: none;
    flex-wrap: nowrap;
}

html.is-mobile .m-appbar-title,
html.is-mobile .home-container > .header h1,
html.is-mobile .orders-wrapper > .header h1 {
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -0.2px;
    color: var(--text-main, #1e293b);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

html.is-mobile .m-appbar-actions,
html.is-mobile .home-container > .header .user-section,
html.is-mobile .orders-wrapper > .header .header-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    width: auto;
    margin-left: auto;
    flex-wrap: nowrap;
}

/* Round icon-only control — back chevron, logout, install. */
html.is-mobile .m-iconbtn,
html.is-mobile .home-container > .header .btn,
html.is-mobile .orders-wrapper > .header .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px; height: 38px;
    flex: none;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: transparent;
    box-shadow: none;
    color: var(--text-muted, #64748b);
    font-size: 1rem;
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease;
}
html.is-mobile .m-iconbtn:active,
html.is-mobile .home-container > .header .btn:active,
html.is-mobile .orders-wrapper > .header .btn:active {
    background: #f1f5f9;
    color: var(--primary, #e07b3c);
    transform: none;
}
html.is-mobile .m-iconbtn.is-danger,
html.is-mobile .home-container > .header #logoutBtn { color: #ef4444; }

/* Install/download is the one app-bar control that's promotional rather than
   a plain utility action (back, logout), so it keeps its "Our App" label and
   a tinted pill — the logo mark's colour treatment, not its round shape,
   since a label needs the width a 38px icon circle doesn't have — instead of
   collapsing to the same flat, colourless bare glyph as everything above. */
html.is-mobile .home-container > .header #installAppBtn {
    width: auto;
    height: 34px;
    padding: 0 12px 0 10px;
    gap: 6px;
    border-radius: 9999px;
    font-size: 0.78rem;
    font-weight: 700;
    background: linear-gradient(135deg, #fdf3e7, #fce8d5);
    color: var(--primary, #e07b3c);
    white-space: nowrap;
}
html.is-mobile .home-container > .header #installAppBtn i { font-size: 0.9rem; }
html.is-mobile .home-container > .header #installAppBtn:active {
    background: linear-gradient(135deg, #fce8d5, #fbdcc0);
    color: var(--primary-hover, #c9692f);
}

/* The café mark, reusing the existing logo circle treatment. */
html.is-mobile .m-appbar-logo,
html.is-mobile .home-container > .header .logo-icon,
html.is-mobile .orders-wrapper > .header .logo-icon {
    width: 34px; height: 34px;
    flex: none;
    border-radius: 50%;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    background: linear-gradient(135deg, #fdf3e7, #fce8d5);
    box-shadow: 0 2px 8px rgba(224, 123, 60, 0.2);
}
html.is-mobile .m-appbar-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
html.is-mobile .home-container > .header .logo-section,
html.is-mobile .orders-wrapper > .header .logo-section { gap: 10px; min-width: 0; }

/* ================================================================
   Bottom tab bar
   ================================================================ */
html.is-mobile .m-tabbar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    /* Above the route loader (2000) on purpose. The bar is the one piece of
       chrome that must survive a page change: this is a multi-page app, so
       every tap reloads the document, and a bar that blinks out behind the
       loader on each navigation is the single thing that gives away that this
       is a website. It is also excluded from the page fade in shared.css. */
    z-index: 2100;
    height: calc(var(--m-tabbar-h) + var(--m-safe-b));
    padding-bottom: var(--m-safe-b);
    background: var(--card, #fff);
    border-top: 1px solid var(--border, #e2e8f0);
    align-items: stretch;
    justify-content: space-around;
}

html.is-mobile .m-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 6px 0 8px;
    text-decoration: none;
    border: none;
    background: none;
    font-family: inherit;
    cursor: pointer;
    color: var(--text-muted, #64748b);
    transition: color 0.2s ease;
}

html.is-mobile .m-tab .m-tab-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px; height: 26px;
    border-radius: 9999px;
    font-size: 1.05rem;
    transition: background 0.22s var(--m-ease), color 0.2s ease;
}
html.is-mobile .m-tab .m-tab-label {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.2px;
}

html.is-mobile .m-tab.is-active { color: var(--primary, #e07b3c); }
html.is-mobile .m-tab.is-active .m-tab-icon {
    background: var(--m-tint, #fdf3e7);
    color: var(--primary, #e07b3c);
}
html.is-mobile .m-tab:active .m-tab-icon { background: #f1f5f9; }

/* ================================================================
   Route loader — stop above the tab bar
   ================================================================ */
/* Only on pages that carry a bar. index/register/order stay full-screen so the
   cold-start splash and the order form are unaffected. */
html.is-mobile body.m-has-tabs .route-loader {
    bottom: calc(var(--m-tabbar-h) + var(--m-safe-b));
}

/* ================================================================
   Shared surfaces and controls
   ================================================================ */
html.is-mobile .m-section-title {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted, #64748b);
    margin: 22px 4px 10px;
}

html.is-mobile .m-formcard {
    background: var(--card, #fff);
    border: 1px solid var(--border, #e2e8f0);
    border-radius: var(--m-card-r);
    padding: 18px 16px 6px;
}

/* Full-width pill buttons — the app-shell default for a primary action. */
html.is-mobile .btn-full {
    width: 100%;
    padding: 15px 20px;
    font-size: 1rem;
    border-radius: 14px;
}
/* Lift and hover-shadow are pointer affordances; on a touch screen they only
   ever fire as a stuck state after a tap. */
html.is-mobile .btn:hover { transform: none; box-shadow: none; }
html.is-mobile .btn-primary,
html.is-mobile .btn-primary:hover {
    background: var(--primary, #e07b3c);
    box-shadow: 0 4px 16px rgba(224, 123, 60, 0.3);
}
html.is-mobile .btn:active { transform: scale(0.985); }

html.is-mobile .form-group label {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted, #64748b);
}
/* Scoped to .input-wrapper, not to every input on the page. The 44px of left
   padding exists to clear the absolutely-positioned .input-icon, and only the
   student pages put their fields in that wrapper. Applied globally it also hit
   the admin side, which has no icons — the time boxes on the settings page
   inherited 44px of dead space, which pushed the digits out of a field 50px
   wide and stretched the row half as tall again. */
html.is-mobile .input-wrapper input,
html.is-mobile .input-wrapper select {
    padding: 15px 14px 15px 44px;
    border-radius: 12px;
}

/* The toast rises from above the tab bar rather than dropping from under the
   status bar, where a notch or the app bar would clip it. */
html.is-mobile .toast {
    top: auto;
    left: var(--m-gutter);
    right: var(--m-gutter);
    bottom: calc(var(--m-safe-b) + 18px);
    max-width: none;
    border-radius: 14px;
    transform: translateY(calc(100% + 90px));
    transition: transform 0.36s var(--m-ease);
}
html.is-mobile body.m-has-tabs .toast { bottom: calc(var(--m-tabbar-h) + var(--m-safe-b) + 14px); }
html.is-mobile .toast.show { transform: translateY(0); }

/* ================================================================
   Bottom sheet
   ================================================================ */
html.is-mobile .m-sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 2200;
    background: rgba(15, 23, 42, 0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.26s ease;
}
html.is-mobile .m-sheet-backdrop.is-open { opacity: 1; pointer-events: auto; }

html.is-mobile .m-sheet {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 2210;
    max-height: 88dvh;
    display: flex;
    flex-direction: column;
    background: var(--card, #fff);
    border-radius: 22px 22px 0 0;
    box-shadow: 0 -8px 40px rgba(15, 23, 42, 0.22);
    padding-bottom: var(--m-safe-b);
    transform: translateY(100%);
    transition: transform 0.34s var(--m-ease);
    touch-action: none;
}
html.is-mobile .m-sheet.is-open { transform: translateY(0); }
/* Set while a finger is down so the panel tracks the gesture instead of
   easing toward it. */
html.is-mobile .m-sheet.is-dragging { transition: none; }

html.is-mobile .m-sheet-handle {
    flex: none;
    padding: 10px 0 6px;
    display: flex;
    justify-content: center;
    cursor: grab;
}
html.is-mobile .m-sheet-handle::before {
    content: '';
    width: 40px; height: 4px;
    border-radius: 9999px;
    background: #cbd5e1;
}
html.is-mobile .m-sheet-body {
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 6px 20px 20px;
    -webkit-overflow-scrolling: touch;
}
html.is-mobile .m-sheet-title {
    font-size: 1.15rem;
    font-weight: 700;
    margin-bottom: 14px;
    color: var(--text-main, #1e293b);
}

/* While a sheet is up the page behind must not scroll. */
html.is-mobile body.m-sheet-open { overflow: hidden; }

/* ================================================================
   index.html / register.html — auth screens
   ================================================================ */
html.is-mobile .login-card,
html.is-mobile .register-card {
    box-shadow: none;
    border: none;
    background: transparent;
    padding: 0;
    border-radius: 0;
    overflow: visible;
}
/* The 5px gradient strip is a card affordance; there is no card here. */
html.is-mobile .login-card::before,
html.is-mobile .register-card::before,
html.is-mobile .profile-card::before,
html.is-mobile .order-card::before { display: none; }

html.is-mobile .login-header,
html.is-mobile .register-header { text-align: center; margin-bottom: 28px; }
html.is-mobile .login-header h1,
html.is-mobile .register-header h1 { font-size: 1.9rem; letter-spacing: -0.6px; }
html.is-mobile .login-header .subtitle,
html.is-mobile .register-header .subtitle { font-size: 0.95rem; }

/* app-shared.js flies the splash logo into `.login-card .logo-icon` by
   measuring it at runtime, and falls back to an abrupt cut if that selector
   stops resolving — so this element is only ever restyled, never removed,
   never display:none, and it keeps both class names. */
html.is-mobile .login-header .logo-icon,
html.is-mobile .register-header .logo-icon {
    width: 60px; height: 60px;
    margin-bottom: 20px;
}

html.is-mobile .bottom-links { margin-top: 26px; }
html.is-mobile .admin-link { margin-top: 18px; }

/* ================================================================
   home.html — the menu
   ================================================================
   The existing .header becomes the app bar rather than a second bar being
   added to the markup: it already holds the logo, the title, the logout button
   and the install button, all of them already wired up. Orders and Profile are
   dropped from it because the tab bar now carries them. */
html.is-mobile .home-container,
html.is-mobile .orders-wrapper { max-width: none; margin: 0; }

/* The menu card loses its box: on a phone the whole screen is the card. */
html.is-mobile .home-container > .card,
html.is-mobile .orders-wrapper > .card {
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
    margin: 0;
}
html.is-mobile .home-container > .card > h2,
html.is-mobile .orders-wrapper > .card > h2 {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted, #64748b);
    margin: 4px 4px 12px;
}

html.is-mobile .menu-grid {
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
html.is-mobile .menu-item {
    background: var(--card, #fff);
    border: 1px solid var(--border, #e2e8f0);
    border-radius: var(--m-card-r);
    padding: 16px 14px 14px;
    min-height: 0;
    box-shadow: var(--shadow-sm, 0 2px 8px rgba(0, 0, 0, 0.05));
    transition: transform 0.16s var(--m-ease);
}
html.is-mobile .menu-item:not(.skeleton-item):active { transform: scale(0.97); }
html.is-mobile .menu-item .emoji {
    font-size: 30px;
    width: 58px; height: 58px;
    margin: 0 auto 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--m-tint, #fdf3e7);
}
html.is-mobile .menu-item .item-name { font-size: 0.92rem; line-height: 1.3; }
html.is-mobile .menu-item .item-price { font-size: 1.05rem; margin-top: 2px; }
html.is-mobile .menu-item .order-btn {
    margin-top: 12px;
    padding: 11px 14px;
    font-size: 0.9rem;
    border-radius: 12px;
}

html.is-mobile #menuClosedMessage {
    background: var(--card, #fff);
    border: 1px solid var(--border, #e2e8f0);
    border-radius: var(--m-card-r);
    padding: 40px 20px !important;
}

/* ================================================================
   Orders view — history
   ================================================================
   .order-row, not .order-card: home.html's merge renamed this row class to
   resolve a collision with the Order view's own .order-card (its one big
   form wrapper) — see the note on .order-row in home.html's <style>. */
html.is-mobile .orders-wrapper .order-row {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    background: var(--card, #fff);
    border-radius: 14px;
    padding: 14px;
    margin-bottom: 10px;
}
html.is-mobile .orders-wrapper .order-row .order-item { font-size: 0.98rem; }
html.is-mobile .orders-wrapper .order-row .order-meta { font-size: 0.8rem; }
html.is-mobile .empty-orders {
    background: var(--card, #fff);
    border: 1px solid var(--border, #e2e8f0);
    border-radius: var(--m-card-r);
    padding: 44px 20px;
    font-style: normal;
}

/* ================================================================
   order.html — place an order
   ================================================================
   The wrapper becomes a full-height flex column so the action bar can sit at
   the bottom of the screen when the form is short (margin-top:auto) and stick
   to the bottom edge when it is long (position:sticky). */
html.is-mobile .order-wrapper {
    max-width: none;
    display: flex;
    flex-direction: column;
    /* The action bar is the last thing on the screen and sits flush with the
       bottom edge, so the page's usual bottom gutter would leave a strip of
       background under it. */
    padding-bottom: 0;
}
html.is-mobile .order-card {
    box-shadow: none;
    border: none;
    background: transparent;
    padding: 0;
    border-radius: 0;
    overflow: visible;
    display: flex;
    flex-direction: column;
    flex: 1;
}
html.is-mobile #orderForm { display: flex; flex-direction: column; flex: 1; }
html.is-mobile .order-header { text-align: left; margin-bottom: 18px; }
html.is-mobile .order-header h1 { font-size: 1.45rem; }
/* Back is the app bar's chevron here, so the in-flow link at the end of the
   card is redundant. */
html.is-mobile .order-wrapper .back-link { display: none; }

/* Not new markup: order.html's existing price summary and submit button are
   wrapped in one .order-actions div, which is an inert block on desktop and
   becomes the action bar here. That keeps #totalPrice a single element in a
   single place, so the page's own updateTotalDisplay() keeps working with no
   mirroring and no duplicate id. */
html.is-mobile .order-actions {
    position: sticky;
    bottom: 0;
    z-index: 800;
    margin: auto calc(-1 * var(--m-gutter)) 0;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px var(--m-gutter) calc(12px + var(--m-safe-b));
    background: var(--card, #fff);
    border-top: 1px solid var(--border, #e2e8f0);
    box-shadow: 0 -4px 20px rgba(15, 23, 42, 0.06);
}
html.is-mobile .order-actions .price-summary {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 0;
    flex: none;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    border-radius: 0;
}
html.is-mobile .order-actions .price-label { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.05em; }
html.is-mobile .order-actions .total-price { font-size: 1.3rem; }
html.is-mobile .order-actions .btn { flex: 1; margin-top: 0; width: auto; }

/* Quantity as a stepper rather than a number spinner. The real input stays in
   the DOM and stays the source of truth — see wireStepper() in
   mobile-shell.js. */
html.is-mobile .m-stepper-host .input-wrapper { display: none; }
html.is-mobile .m-stepper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    background: #f8fafc;
    border: 1px solid var(--border, #e2e8f0);
    border-radius: 12px;
    padding: 6px;
}
html.is-mobile .m-stepper button {
    width: 42px; height: 42px;
    flex: none;
    border: none;
    border-radius: 10px;
    background: var(--card, #fff);
    color: var(--primary, #e07b3c);
    font-size: 1rem;
    font-family: inherit;
    cursor: pointer;
    box-shadow: var(--shadow-sm, 0 2px 8px rgba(0, 0, 0, 0.05));
}
html.is-mobile .m-stepper button:disabled { opacity: 0.4; cursor: not-allowed; }
html.is-mobile .m-stepper button:active:not(:disabled) { transform: scale(0.94); }
html.is-mobile .m-stepper .m-stepper-value {
    flex: 1;
    text-align: center;
    font-size: 1.1rem;
    font-weight: 700;
}

/* ================================================================
   profile.html — account
   ================================================================ */
html.is-mobile .profile-wrapper { max-width: none; }
html.is-mobile .profile-card {
    box-shadow: none;
    border: none;
    background: transparent;
    padding: 0;
    border-radius: 0;
    overflow: visible;
}
html.is-mobile .profile-header { margin-bottom: 20px; text-align: center; }
html.is-mobile .profile-header h1 { font-size: 1.5rem; }
html.is-mobile .profile-header .logo-icon { width: 76px; height: 76px; font-size: 40px; }
html.is-mobile .profile-wrapper .back-link { display: none; }

/* ================================================================
   Page transitions
   ================================================================
   Direction comes from mobile-shell.js, which sets data-m-nav on <body> in a
   capture-phase click listener — before the page's own handler calls goTo() —
   and carries the same value to the next document in sessionStorage.

   goTo() and the café-mark handoff are untouched; this only adds the
   horizontal half of a movement that already fades. Keep the distance small:
   the page underneath is opaque, so a long slide reads as a glitch rather
   than a push.

   .m-no-slide opts a page out. index.html carries it because its cold-start
   splash measures where `.login-card .logo-icon` has landed and flies the logo
   there — a wrapper sitting 24px off at that moment sends the logo to the
   wrong place and then snaps. */
html.is-mobile body.page-leaving[data-m-nav="forward"] > .m-page:not(.m-no-slide) { transform: translateX(-24px); }
html.is-mobile body.page-leaving[data-m-nav="back"] > .m-page:not(.m-no-slide) { transform: translateX(24px); }
html.is-mobile body:not(.page-ready)[data-m-enter="forward"] > .m-page:not(.m-no-slide) { transform: translateX(24px); }
html.is-mobile body:not(.page-ready)[data-m-enter="back"] > .m-page:not(.m-no-slide) { transform: translateX(-24px); }

/* Hold the offset still on the way in.
   data-m-enter is written by mobile-shell.js, which is a module and therefore
   runs after the first paint — so the page starts at zero and the attribute
   arriving is a *change*, which the transform transition would then animate.
   The page would slide 24px out and come back, which is the opposite of the
   movement intended. Snapping to the offset and animating only the return is
   what makes it read as arriving from the side. Opacity is respecified rather
   than dropped, or the entrance fade from shared.css would go with it. */
html.is-mobile body:not(.page-ready) > .m-page { transition: opacity 0.28s ease; }

/* And let the outgoing one actually travel. shared.css narrows the transition
   to opacity alone while a page is leaving — at a specificity this has to beat
   — so without this the exit offset lands in a single frame. */
html.is-mobile body.page-leaving > .m-page:not(.m-no-slide) {
    transition: transform 220ms var(--m-ease), opacity 0.18s ease;
}

/* shared.css already zeroes every transition duration under reduced motion, so
   these would land instantly rather than break — but the chrome should not be
   displaced at all in that case. */
@media (prefers-reduced-motion: reduce) {
    html.is-mobile .m-page,
    html.is-mobile .m-sheet { transform: none !important; }
    html.is-mobile .m-sheet:not(.is-open) { visibility: hidden; }
}
