﻿/* =============================================================================
   mesero.css — the Mesero surface's own stylesheet (#54, phase 2 of #52)
   =============================================================================

   The stated goal of the extraction is "one master sheet for the WebApp, one for
   the Mesero UX". This is the second sheet. It did not exist before, which is why
   #54 was blocked: there was nowhere to move Mesero's 3,700 inline lines TO.

   `mobile.css` is NOT this file, despite the name. All 10 of its rules are scoped
   `body.mobile-layout .app-layout` — it is admin-on-a-small-screen, and its own
   comment says it is scoped so it can never reach the Mesero pages' deliberately
   smaller inputs. The two surfaces are unrelated.

   ---------------------------------------------------------------------------
   SCOPE: `.empty-layout`, and why that is the right boundary
   ---------------------------------------------------------------------------
   Every Mesero page declares `@layout EmptyLayout`, which renders
   `<div class="empty-layout">` (Layout/EmptyLayout.razor:18). Admin pages get
   MainLayout's `.app-layout` / `.public-layout`. That split is what
   docs/architecture/webapp-surface-boundary-contract.md governs, and it is
   already how app.css keeps the admin field primitives off this surface
   (app.css ~:3074, GH-26).

   Declaring the token layer ON `.empty-layout` rather than at `:root` means these
   names simply do not exist on the admin surface. A Mesero token cannot leak into
   admin, and an admin page that tries to consume one gets nothing rather than
   something plausible-but-wrong.

   Seven pages are in scope — note this is TWO MORE than #54 originally listed:

       Pages/Mobile/MobileTabDetail.razor      1275 css lines
       Pages/Mobile/MobileOrders.razor          910
       Pages/Mobile/MobileNewOrder.razor        757
       Pages/MobileAuthLink.razor               372   <- omitted from #54
       Pages/Mobile/MobileShiftEnd.razor        264
       Pages/Mobile/MobileLogout.razor           63
       Pages/MobileAuthCancelled.razor           63   <- omitted from #54

   ---------------------------------------------------------------------------
   THE TOKENS BELOW ARE RATIFIED, NOT INVENTED
   ---------------------------------------------------------------------------
   Light values are copied from docs/architecture/mesero-ui-guide.md §1.2. Dark
   values come from its §1.3 semantic table, which is the 2026-08-08 ruling:
   **navy is the light-theme brand, coral is the dark-theme brand.** Navy #000075
   is near-black and cannot carry emphasis on a dark surface; coral is a mid-tone
   that reads on both but stays reserved in light.

   Three rules from that guide govern every consumer:
     1. No bare hex in a Mesero <style> block — consume a token.
     2. Never write `var(--m-x, #fallback)`. A missing token must break loudly.
     3. Never declare a custom property in a page <style>. Declaration lives here.

   ---------------------------------------------------------------------------
   WHY THE DARK SURFACES ARE SLATE
   ---------------------------------------------------------------------------
   Mesero currently ships TWO dark palettes, which is a defect this file resolves:

       MobileOrders + MobileTabDetail   --bg-primary #0f172a  --text-primary #f9fafb
                                        --text-secondary #9ca3af        (slate)
       MobileShiftEnd                   --pt-bg #0e0e1c  --pt-card-bg #1a1a2e
                                        --pt-text #f0f0f5  --pt-border #2e2e44  (violet)

   Slate wins: it is the majority (two files to one) and it is the same family as
   app.css's own dark palette (--background-color #0f172a), so the two surfaces
   stay recognisably one product. MobileShiftEnd's violet is the outlier and gets
   reconciled when that file moves.

   Both of those sets are also declared INSIDE page <style> blocks, violating rule
   3 above, and they shadow real app.css globals (`--text-primary`,
   `--text-secondary`) with DIFFERENT values. Worse, six of MobileShiftEnd's names
   (`--pt-text`, `--pt-bg`, `--pt-border`, `--pt-card-bg`, `--pt-input-bg`,
   `--pt-text-muted`) squat the shared `--pt-` namespace on names app.css does not
   define — so that page renders its entire LIGHT theme from inline fallbacks, and
   the day app.css defines `--pt-bg` at :root, its light mode silently flips while
   dark keeps working. Renaming those to `--m-*` as the file moves is the fix.

   ---------------------------------------------------------------------------
   STATUS: this file currently declares ONLY. No page consumes it yet, so adding
   it is a no-op — nothing renders differently until a page is migrated. That is
   deliberate: the token layer has to exist before anything can move onto it.
   ============================================================================= */

.empty-layout {
    /* ---- Brand — SWAPS BY THEME (guide §1.3). Navy in light, coral in dark. ----
       One token, consumed by every primary CTA and every emphasis foreground, so
       the theme swap happens here rather than at ~54 call sites. */
    --m-brand:        #000075;
    --m-brand-hover:  #1a1a8f;
    --m-on-brand:     #ffffff;

    /* ---- Chrome — structural navy that does NOT swap ----
       Top-bar and sheet-header BACKGROUNDS, structural borders. A navy background
       stays navy in dark; only navy-as-a-foreground swaps. */
    --m-chrome:       #000075;
    --m-on-chrome:    #ffffff;

    /* Secondary action (outline / inverted). Coral in light. */
    --m-secondary-fg: var(--m-coral);

    /* Coral as a literal, for the light-mode secondary only. */
    --m-coral:        #ec607e;
    --m-coral-hover:  #d94d6a;
    /* Text ON a coral fill. Navy, never white: white on coral is 3.21:1, under AA for
       small text; navy on coral is 5.20:1. Coral does not swap by theme, so neither
       does this (MC4 — the claim card's "Terminal propia" pill was white on coral). */
    --m-on-coral:     #000075;

    /* ---- State. Guide §1 values, deliberately NOT app.css's. ----

       --m-warn is the FILL: state pills, borders, the tint's own colour. It is not
       usable as a foreground ON that tint — #f59e0b on #fef3c7 is 1.93:1 and the
       glyph nearly vanishes. --m-warn-fg exists for that, mirroring --m-danger-fg,
       and measures 4.60:1 so it is safe for text as well as icons (#78).

       Before this token, MobileAuthLink's expired icon carried a hardcoded #d97706
       at 2.86:1 — kept only because the token was worse. Both were wrong. */
    --m-warn:         #f59e0b;
    --m-warn-fg:      #996206;
    --m-warn-bg:      #fef3c7;
    --m-danger:       #dc2626;
    --m-danger-fg:    #dc2626;
    --m-danger-bg:    #fee2e2;
    /* Added for MobileTabDetail (#54 Phase B). Its --pt-destructive (#ef4444) and
       --pt-destructive-hover (#dc2626) are base and hover; --m-danger is #dc2626,
       so mapping both onto it would make hover identical to base and silently
       delete the state. Completes the pattern --m-brand-hover / --m-coral-hover
       already establish. */
    --m-danger-hover: #b91c1c;
    --m-positive:     #16a34a;
    --m-positive-fg:  #16a34a;
    --m-positive-bg:  #dcfce7;
    --m-on-positive:  #ffffff;

    /* ---- Surfaces ---- */
    --m-bg:           #f8fafc;
    --m-surface:      #ffffff;
    --m-surface-2:    #f1f5f9;
    --m-border:       #e5e7eb;

    /* ---- Text. Named --m-fg, NOT --text-primary: that is an app.css global
       (app.css:278) and Mesero shadows it with a different value today, which is
       precisely the collision this namespace removes.

       MEASURED, because a token layer that quietly licenses unreadable text is
       worse than no token layer (contrast against --m-surface / --m-bg / --m-surface-2):

           --m-fg          17.74 / 16.96 / 16.19   body text, fine anywhere
           --m-fg-muted     4.98 /  4.76 /  4.55   AA on all three

       --m-fg-muted was the guide's #6b7280, which measured 4.41:1 on --m-surface-2
       — under AA on one of the three surfaces, so the token could not be used where
       its name said it could. #69707d clears all three and is imperceptibly
       different (#78). Dark is unchanged; #9ca3af was already 4.95–7.03 there.

       THE THIRD TEXT GREY IS GONE. The guide's --m-fg-subtle (#9ca3af) measured
       2.54 / 2.43 / 2.32 — it failed even the 3:1 non-text floor, so no size of
       text could legally use it, and darkening it to reach 4.5:1 landed on #677180,
       which is --m-fg-muted with extra steps. It is renamed --m-hairline below and
       is no longer part of the text scale. ---- */
    --m-fg:           #111827;
    --m-fg-muted:     #69707d;

    /* ---- Decorative rules only. NOT a text or icon colour. ----
       2.54 / 2.43 / 2.32:1 against the three surfaces. That clears nothing: body
       text needs 4.5, and an icon or control boundary that carries meaning needs
       3:1 under WCAG 1.4.11. The one thing it IS legal for is a purely decorative
       separator, which that success criterion explicitly exempts — hence the name.

       Distinct from --m-border (#e5e7eb, 1.24:1) on purpose: that is a container
       edge you are not meant to notice, this is a divider you are. If you find
       yourself reaching for this for a glyph, you want --m-fg-muted. (#78) ---- */
    --m-hairline:     #9ca3af;

    /* ---- Geometry. Straight edges are the brand rule; 44px is the touch floor
       this surface is held to (app.css's --touch-target-min is 40px and is NOT
       the same commitment — see the note at --pt-field-min-height). ---- */
    --m-radius:       0;
    --m-touch-min:    44px;

    /* ---- Stacking. The scale mesero-ui-guide.md §3.1 ratified, declared the day
       the first primitive needed it (MSheet, Mesero close parity MC3). Today's
       literals — 100/200/210/250/260/300/1150 — stay where they are until their
       own surfaces move; only --m-z-sheet has a consumer so far. ---- */
    --m-z-sticky:     100;
    --m-z-fab:        110;
    --m-z-popover:    200;
    --m-z-sheet:      300;
    --m-z-toast:      400;
}

@media (prefers-color-scheme: dark) {
    .empty-layout {
        /* The ruling: coral becomes the brand in dark. Navy cannot carry emphasis
           against a dark surface — #000075 on #0f172a is 1.2:1. */
        --m-brand:        #ec607e;
        --m-brand-hover:  #d94d6a;
        /* NOT white. White on coral is 3.21:1, under the 4.5:1 AA floor for a 16px label —
           the admin surface's #46, resolved there by changing the LABEL, not the fill: navy
           on coral is 5.20:1. The guide's block ratified #ffffff before anything put text
           on this token; the first thing that did (the Cobrar sheet's CTA, MC3) measured
           it. */
        --m-on-brand:     #000075;

        /* Chrome does not swap — a navy bar stays a navy bar. */
        --m-chrome:       #000075;
        --m-on-chrome:    #ffffff;

        /* Secondary in dark is navy, and ONLY on a light surface. Measured: 16.71:1
           on white, but **1.14:1 on --m-surface**. Consuming this against a dark
           Mesero surface is an accessibility bug, not a style choice — it is the
           one token here that is unsafe by default in its own theme. */
        --m-secondary-fg: #000075;

        /* State — lightened so they read against a dark surface. */
        --m-danger:       #f87171;
        --m-danger-fg:    #f87171;
        --m-danger-bg:    rgba(220, 38, 38, 0.18);
        /* Hover LIGHTENS in dark, the mirror of darkening in light. */
        --m-danger-hover: #fca5a5;
        --m-positive:     #34d399;
        --m-positive-fg:  #34d399;
        --m-positive-bg:  rgba(22, 163, 74, 0.18);
        /* Dark's green is LIGHTER, so its foreground goes dark — white would be 1.96:1. */
        --m-on-positive:  #0f172a;
        --m-warn:         #f59e0b;
        /* The foreground SWAPS, like --m-danger does. #996206 is 4.60:1 on the light
           tint but only 2.15:1 on the dark one; #f59e0b is 5.11:1 there. A single
           value cannot serve both, which is why this is its own token. */
        --m-warn-fg:      #f59e0b;
        --m-warn-bg:      rgba(245, 158, 11, 0.18);

        /* Surfaces — the slate family, matching what MobileOrders and
           MobileTabDetail already ship and app.css's own dark palette. */
        --m-bg:           #0f172a;
        --m-surface:      #1e293b;
        /* Deliberately the same value as --m-bg, and NOT a mistake. --m-surface-2 is
           the RECESSED tone: in light it is #f1f5f9, darker than the #ffffff surface.
           Keeping it recessed in dark means going darker than the #1e293b card, which
           lands on the page colour. An earlier draft here used #263449 — lighter than
           the card — which inverted the relationship and made the same token raised in
           dark and recessed in light. It also measured worse (--m-fg-muted 4.95:1 vs
           7.03:1). Matches mesero-ui-guide.md §1.2's dark block. */
        --m-surface-2:    #0f172a;
        --m-border:       #334155;

        --m-fg:           #f9fafb;
        --m-fg-muted:     #9ca3af;
        --m-hairline:     #64748b;
    }
}

/* =============================================================================
   PATTERNS
   =============================================================================
   Rules move here from page <style> blocks as each page is migrated (#54 step 2).
   Everything below consumes the token layer above; nothing declares.

   Class names carry the `m-` prefix (guide rule M-0.1). The prefix IS the
   namespace — these are plain (0,1,0) selectors rather than `.empty-layout X`,
   because a name that cannot collide does not need scoping to defend it. The
   TOKENS are scoped to `.empty-layout`, so if one of these classes is ever used
   on the admin surface it renders unstyled rather than half-styled. That is the
   guide's "a missing token must break loudly", inherited for free.
   ============================================================================= */

/* ---- Full-screen status page ------------------------------------------------
   The /m/logout and /m/cancelled screens. They were two near-identical copies:
   same structure, same box-shadow, same type scale, differing only in a class
   prefix (`logout-` / `cancelled-`), the icon glyph, and the icon tint. Both
   also styled bare `h2` and `p` selectors from a global inline block.

   ONE deliberate appearance change, and it is a fix: `.hint` shipped
   `color: #9ca3af`, which is 2.54:1 on the white card in light and 3.04:1 in
   dark. That is hint TEXT failing AA in both themes on both pages. It now takes
   --m-fg-muted: 4.83:1 and 5.76:1. Everything else is within a hex digit or two
   of what shipped (h2 #1f2937 -> --m-fg #111827, card #1f2937 -> --m-surface
   #1e293b in dark) — both imperceptible, both moving onto the shared palette. */
.m-status-screen {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: var(--m-chrome);
}

.m-status-card {
    background: var(--m-surface);
    border-radius: var(--m-radius);
    box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
    width: 100%;
    max-width: 400px;
    padding: 3rem 2rem;
    text-align: center;
    border-bottom: 4px solid var(--m-coral);
}

.m-status-icon {
    width: 80px;
    height: 80px;
    border-radius: var(--m-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 1.5rem;
    background: var(--m-surface-2);
}

.m-status-icon i {
    font-size: 2.5rem;
    color: var(--m-fg-muted);
}

/* Accent variant — the logout screen's coral. The tint is a literal rather than a
   token because the vocabulary has no alpha-tint entry; adding --m-coral-tint is
   a vocabulary decision for the guide, not one to take here. */
.m-status-icon--accent {
    background: rgba(236, 96, 126, 0.15);
}

.m-status-icon--accent i {
    color: var(--m-coral);
}

.m-status-title {
    margin: 0 0 0.75rem 0;
    font-size: 1.5rem;
    color: var(--m-fg);
}

/* GROUPED ON PURPOSE, and this is the trap in migrating a bare element selector.
   The originals styled `p { margin: 0 0 .5rem 0; line-height: 1.5 }` and then
   `.hint { font-size; color }` on top — and the hint was a `<p class="hint">`, so
   it inherited the paragraph rule. Splitting them into two explicit classes drops
   that inheritance silently: `.m-status-hint` alone gets the UA default `1em 0`
   margin and default line-height, which is visibly more space around the hint.
   Nothing errors, nothing fails a build, the page just spreads out.

   Rule for the remaining five files: when a bare element selector goes away, its
   declarations have to be re-homed onto EVERY class that was relying on it, not
   only the one that obviously replaces it. */
.m-status-text,
.m-status-hint {
    margin: 0 0 0.5rem 0;
    color: var(--m-fg-muted);
    line-height: 1.5;
}

/* Was `.hint { color: #9ca3af }` — 2.54:1. See the note above. */
.m-status-hint {
    font-size: 0.85rem;
}

@media (prefers-color-scheme: dark) {
    /* The surface and text tokens already swap, so only the accent tint needs a
       dark value — it was bumped 0.15 -> 0.2 in the original to hold up against
       the darker card. */
    .m-status-icon--accent {
        background: rgba(236, 96, 126, 0.2);
    }
}

/* ---- Shift-end screen (/m/shift-end) ---------------------------------------
   Migrated from MobileShiftEnd.razor (#54 step 2). 264 inline lines.

   WHY THIS FILE WENT SECOND despite not being the smallest: it carried the
   poisoned --pt-* declarations. Its dark block declared six names ON
   `.mobile-shift-end` — --pt-bg, --pt-card-bg, --pt-text, --pt-text-muted,
   --pt-border, --pt-input-bg — none of which app.css defines, while its own
   comment claimed they were "tokenised vars set on :root by the theme switcher".
   They were not. Two consequences, both now gone:

     - LIGHT mode had no declarations at all, so every one of those var() calls
       fell through to its inline fallback. The page rendered its entire light
       theme from `var(--pt-text, #111)`-style defaults.
     - The names squat the shared --pt- namespace. The day app.css defines
       --pt-bg at :root, this page's light mode silently flips while dark keeps
       working.

   It also carried the second of Mesero's two dark palettes — violet (#0e0e1c,
   #1a1a2e, #2e2e44) against the slate every other page uses. Now on the shared
   --m-* layer, so that split is closed.

   CLASS NAMES are `m-se-*`: the `m-` prefix per guide rule M-0.1, `se` keeping
   this page's existing namespace. Seven of its old names — spinner,
   loading-state, error-state, btn-back, btn-retry, header-title, warn — are also
   defined by two or three OTHER Mesero pages. That is invisible today because an
   inline <style> exists only while its page renders, but the moment two of those
   pages share this sheet the copies would fight. Prefixing keeps each move a
   no-op; unifying is a later pass, once more than one version is in here to
   compare. See the note at the end of this block.

   NAVY MAPS TO --m-chrome THROUGHOUT, deliberately. Guide §1.3 would have a
   primary CTA and an emphasis foreground take --m-brand, which swaps to coral in
   dark — so by that reading `.m-se-cta` and `.m-se-table-num` should swap. Neither
   swapped during the file move: that was a design change, and a move is not where
   design changes belong. Raised on #54 instead.

   Half of it has since been decided. #216 reported the table numbers as unreadable
   in dark — navy ink on the dark card — so `.m-se-table-num` now takes --m-brand and
   swaps. `.m-se-cta` deliberately still does NOT: nobody reported the button, and
   turning the shift-closing CTA coral in dark is a visible change no card asked for.
   That half stays open. */

.m-se-page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background: var(--m-bg);
    color: var(--m-fg);
    font-family: 'Montserrat', system-ui, -apple-system, sans-serif;
    font-variant-numeric: tabular-nums;
}

.m-se-header {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    background: var(--m-chrome);
    color: var(--m-on-chrome);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.m-se-header-title h1 {
    margin: 0;
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    font-size: 1.0625rem;
    color: var(--m-on-chrome);
}

.m-se-header-spacer {
    flex: 1;
}

.m-se-content {
    flex: 1;
    padding: 0.75rem 0.75rem 6rem; /* bottom-pad clears the sticky footer */
}

.m-se-section-title {
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    font-size: 0.8125rem;
    margin: 0 0 0.5rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--m-fg-muted);
}

.m-se-summary {
    background: var(--m-surface);
    padding: 0.75rem;
    margin-bottom: 1rem;
    border-left: 3px solid var(--m-chrome);
}

.m-se-summary-rows {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.m-se-summary-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
}

.m-se-summary-row.m-se-warn {
    color: var(--m-coral);
    font-weight: 600;
}

.m-se-summary-row i {
    width: 16px;
    text-align: center;
}

.m-se-empty {
    text-align: center;
    padding: 2rem 1rem;
    background: var(--m-surface);
    color: var(--m-fg-muted);
}

.m-se-empty i {
    font-size: 2.5rem;
    color: var(--m-coral);
    margin-bottom: 0.75rem;
}

.m-se-empty p {
    margin: 0;
    font-size: 0.9375rem;
}

.m-se-notes-help {
    margin: 0 0 0.75rem;
    font-size: 0.8125rem;
    color: var(--m-fg-muted);
}

.m-se-tab-row {
    background: var(--m-surface);
    padding: 0.75rem;
    margin-bottom: 0.625rem;
    border-left: 3px solid transparent;
}

.m-se-tab-row:has(.m-se-badge-cash) {
    border-left-color: var(--m-coral);
}

.m-se-tab-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    flex-wrap: wrap;
}

.m-se-tab-id {
    display: flex;
    align-items: baseline;
    gap: 0.25rem;
    flex-wrap: wrap;
}

.m-se-table-num {
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    font-size: 0.9375rem;
    /* #216 — --m-brand, not --m-chrome. Chrome stays navy in dark on purpose, which makes it a
       background-only token; as ink on the dark card "Mesa P1" was navy on slate. --m-brand is
       the foreground half of that split and swaps to coral in dark. */
    color: var(--m-brand);
}

.m-se-customer-name {
    font-size: 0.8125rem;
    color: var(--m-fg-muted);
}

.m-se-badges {
    display: flex;
    gap: 0.25rem;
    flex-wrap: wrap;
}

/* `color: var(--m-chrome)` is navy ink, which is exactly what --pt-on-coral
   resolved to (app.css defines it as var(--pt-navy)). Navy on coral is the
   branded pairing ratified in #46 at 5.20:1. */
.m-se-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.125rem 0.375rem;
    font-size: 0.6875rem;
    font-weight: 600;
    border-radius: var(--m-radius);
    background: var(--m-coral);
    color: var(--m-chrome);
}

.m-se-badge.m-se-badge-call {
    background: var(--m-chrome);
    color: var(--m-on-chrome);
}

.m-se-textarea {
    width: 100%;
    min-height: 72px;
    padding: 0.5rem;
    border: 1px solid var(--m-border);
    border-radius: var(--m-radius);
    font-family: 'Montserrat', sans-serif;
    /* >= 16px or iOS auto-zooms the webview on focus and never zooms back out —
       see the note on .instructions-section textarea in MobileNewOrder. */
    font-size: 16px;
    background: var(--m-surface);
    color: var(--m-fg);
    resize: vertical;
}

.m-se-textarea:focus {
    outline: 2px solid var(--m-chrome);
    outline-offset: -1px;
}

.m-se-textarea.m-se-over-limit {
    border-color: var(--m-coral);
}

.m-se-textarea:disabled {
    opacity: 0.6;
}

.m-se-char-counter {
    text-align: right;
    font-size: 0.6875rem;
    color: var(--m-fg-muted);
    margin-top: 0.25rem;
}

.m-se-footer {
    position: sticky;
    bottom: 0;
    background: var(--m-surface);
    padding: 0.625rem 0.75rem;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
    z-index: 90;
}

.m-se-progress {
    text-align: center;
    font-size: 0.8125rem;
    color: var(--m-fg-muted);
    margin-bottom: 0.375rem;
}

.m-se-cta {
    width: 100%;
    min-height: 48px;
    background: var(--m-chrome);
    color: var(--m-on-chrome);
    border: none;
    border-radius: var(--m-radius);
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    font-size: 0.9375rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

.m-se-cta:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.m-se-loading,
.m-se-error {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
    text-align: center;
    color: var(--m-fg-muted);
}

.m-se-spinner {
    width: 36px;
    height: 36px;
    border: 3px solid var(--m-border);
    border-top-color: var(--m-chrome);
    border-radius: 50%;
    animation: m-spin 0.8s linear infinite;
    margin-bottom: 0.75rem;
}

/* Keyframe names are scoped by NOTHING — not by Blazor isolation, not by a class
   prefix — so a page-local `spin` collides with every other Mesero page's `spin`
   the moment they share this sheet. Prefixed for the same reason the classes are. */
@keyframes m-spin {
    to { transform: rotate(360deg); }
}

.m-se-error i {
    font-size: 2.5rem;
    color: var(--m-coral);
    margin-bottom: 0.5rem;
}

.m-se-btn-retry {
    margin-top: 0.75rem;
    min-height: var(--m-touch-min);
    padding: 0.5rem 1rem;
    background: var(--m-chrome);
    color: var(--m-on-chrome);
    border: none;
    border-radius: var(--m-radius);
    font-weight: 600;
    cursor: pointer;
}

/* UNIFICATION CANDIDATES, measured across all four pages before this move:

     .loading-state   7/7/8/7 declarations, every one opening `flex:1; display:flex;
                      flex-direction:column; align-items:center` — the same concept
                      four times. Unify when a second page lands here.
     .error-state     same shape, same conclusion.
     .btn-retry       MobileOrders and MobileShiftEnd are identical; MobileTabDetail
                      differs (10 decls, different layout). Two of three unify.
     .spinner         DIVERGES, and one branch is a defect: MobileOrders and
                      MobileTabDetail set border-top-color #3b82f6 — a blue that
                      appears nowhere in the Mesero palette. Reconcile onto a token
                      rather than picking a winner.
     .btn-back        MobileNewOrder/MobileTabDetail are 14 declarations, this page 7.
                      Genuinely different buttons sharing a name.
     .header-title    MobileNewOrder/MobileTabDetail identical; this page only ever
                      styled the descendant h1. */

/* ---- Waiter auth-link screen (/m/link) --------------------------------------
   Migrated from MobileAuthLink.razor (#54 step 2). 372 inline lines — the file
   this issue omitted entirely from its table.

   Four of its class names collided with **app.css**, not just with other Mesero
   pages: `btn`, `btn-success`, `info-label`, `info-value`. The first two are
   Bootstrap's and are RENDERED as Bootstrap classes, so they cannot be renamed —
   they stay as ancestor-scoped overrides (`.m-al-actions .btn`), which is how the
   page already wrote them and which keeps them off every other surface.
   `info-label` / `info-value` are the page's own and are renamed.

   `error-icon`, `expired-icon` and `waiter-avatar` collide with MobileOrders and
   MobileTabDetail — the same seven-way problem recorded on the shift-end block.
   Namespaced rather than unified, for the same reason.

   BOTH @keyframes are prefixed. `shake` and `successPop` are scoped by nothing at
   all, and `shake` in particular is the kind of generic name a second page will
   define. */

.m-al-screen {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: var(--m-chrome);
}

.m-al-card {
    background: var(--m-surface);
    border-radius: var(--m-radius);
    box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
    width: 100%;
    max-width: 400px;
    overflow: hidden;
    border-bottom: 4px solid var(--m-coral);
}

/* ---- loading ---- */
.m-al-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 4rem 2rem;
    gap: 1rem;
    color: var(--m-fg-muted);
}

.m-al-loading .spinner-border {
    width: 3rem;
    height: 3rem;
}

/* ---- error / expired ---- */
.m-al-error,
.m-al-expired {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 3rem 2rem;
}

.m-al-error-icon,
.m-al-expired-icon {
    width: 80px;
    height: 80px;
    border-radius: var(--m-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 1.5rem;
}

.m-al-error-icon {
    background: var(--m-danger-bg);
}

.m-al-error-icon i {
    font-size: 2.5rem;
    color: var(--m-danger);
}

.m-al-expired-icon {
    background: var(--m-warn-bg);
}

/* Was a hardcoded #d97706 at 2.86:1 — under the 3:1 non-text floor, kept only
   because --m-warn was worse at 1.93:1. --m-warn-fg (#78) exists for exactly this
   position and measures 4.60:1 light / 5.11:1 dark. */
.m-al-expired-icon i {
    font-size: 2.5rem;
    color: var(--m-warn-fg);
}

.m-al-error h2,
.m-al-expired h2 {
    margin: 0 0 0.75rem 0;
    font-size: 1.5rem;
    color: var(--m-fg);
}

.m-al-error p,
.m-al-expired p {
    margin: 0 0 1.5rem 0;
    color: var(--m-fg-muted);
    line-height: 1.5;
}

.m-al-lockout {
    background: var(--m-danger-bg);
    color: var(--m-danger);
    padding: 0.75rem 1rem;
    border-radius: var(--m-radius);
    font-size: 0.9rem;
    margin-top: 0.5rem;
}

/* ---- confirm / PIN ---- */
.m-al-confirm {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 2rem;
}

.m-al-logo {
    margin-bottom: 1.5rem;
}

.m-al-logo img {
    height: 40px;
    width: auto;
}

.m-al-welcome {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 1rem;
}

.m-al-avatar {
    width: 80px;
    height: 80px;
    border-radius: var(--m-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--m-on-chrome);
    font-weight: 700;
    font-size: 1.75rem;
    margin-bottom: 1rem;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    border-bottom: 3px solid var(--m-coral);
}

.m-al-welcome h2 {
    margin: 0;
    font-size: 1.5rem;
    color: var(--m-fg);
}

/* Back on --m-fg-muted, which is what this secondary line always wanted. It was
   forced onto --m-fg because muted measured 4.41:1 on --m-surface-2 — the one
   pairing in the layer that missed AA. #78 darkened muted to #69707d, so the
   exception is gone and the semantic choice is available again. */
.m-al-restaurant {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    background: var(--m-surface-2);
    border-radius: var(--m-radius);
    font-size: 0.9rem;
    color: var(--m-fg-muted);
    margin-bottom: 1.5rem;
}

.m-al-restaurant i {
    color: var(--m-coral);
}

.m-al-confirm-message {
    margin: 0 0 1.5rem 0;
    font-size: 1rem;
    color: var(--m-fg-muted);
    line-height: 1.5;
}

.m-al-pin-entry {
    margin-bottom: 1.5rem;
    width: 100%;
}

.m-al-pin-inputs {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
}

/* width is --m-touch-min: these are the PIN boxes and they are the touch target. */
.m-al-pin-digit {
    width: var(--m-touch-min);
    height: 56px;
    border: 2px solid var(--m-border);
    border-radius: var(--m-radius);
    text-align: center;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--m-fg);
    background: var(--m-surface);
    transition: all 0.2s ease;
    -webkit-text-security: disc;
}

.m-al-pin-digit:focus {
    outline: none;
    border-color: var(--m-coral);
    box-shadow: 0 0 0 3px rgba(236, 96, 126, 0.2);
}

.m-al-pin-digit.m-al-pin-error {
    border-color: var(--m-danger);
    animation: m-al-shake 0.4s ease;
}

@keyframes m-al-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-8px); }
    75% { transform: translateX(8px); }
}

.m-al-pin-error-message {
    margin: 0.75rem 0 0 0;
    color: var(--m-danger);
    font-size: 0.9rem;
}

/* ---- actions. The .btn / .btn-success overrides stay ancestor-scoped because
   the markup renders Bootstrap's classes and they cannot be renamed. Scoping is
   what keeps them off every other Mesero screen. ---- */
.m-al-actions {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    width: 100%;
    margin-bottom: 1.5rem;
}

.m-al-actions .btn {
    padding: 0.875rem 1.5rem;
    border-radius: var(--m-radius);
    font-weight: 600;
}

.m-al-actions .btn-success {
    background: var(--m-coral);
    border: none;
}

.m-al-actions .btn-success:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.m-al-security-note {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    background: rgba(0, 0, 117, 0.1);
    border-radius: var(--m-radius);
    font-size: 0.8rem;
    color: var(--m-chrome);
}

/* ---- success ---- */
.m-al-success {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 3rem 2rem;
}

.m-al-success-icon {
    width: 100px;
    height: 100px;
    /* #206 — "Acceso Activado" is a success, and success is green here as it is on the
       Entregado pill. Coral is the brand accent and says nothing about the outcome. */
    background: var(--m-positive);
    border-radius: var(--m-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 1.5rem;
    animation: m-al-success-pop 0.5s ease-out;
}

.m-al-success-icon i {
    font-size: 3rem;
    color: var(--m-on-positive);
}

@keyframes m-al-success-pop {
    0% { transform: scale(0); opacity: 0; }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); opacity: 1; }
}

.m-al-success h2 {
    margin: 0 0 0.5rem 0;
    font-size: 1.5rem;
    color: var(--m-fg);
}

.m-al-success p {
    margin: 0 0 1.5rem 0;
    color: var(--m-fg-muted);
}

.m-al-session {
    width: 100%;
    background: var(--m-bg);
    border-radius: var(--m-radius);
    padding: 1rem;
    margin-bottom: 1.5rem;
}

.m-al-info-row {
    display: flex;
    justify-content: space-between;
    padding: 0.5rem 0;
}

.m-al-info-row:not(:last-child) {
    border-bottom: 1px solid var(--m-border);
}

.m-al-info-label {
    color: var(--m-fg-muted);
    font-size: 0.9rem;
}

.m-al-info-value {
    color: var(--m-fg);
    font-weight: 600;
    font-size: 0.9rem;
}

.m-al-success .btn-lg {
    padding: 1rem 2rem;
    border-radius: var(--m-radius);
    font-weight: 600;
    width: 100%;
    background: var(--m-chrome);
    border: none;
}

/* ---- dark ----
   Nine of the original's fifteen dark rules are gone: card surface, headings,
   body copy, restaurant strip, PIN field, session panel, dividers and both info
   lines all now track --m-* and need no override. What remains is genuinely
   theme-specific. */
@media (prefers-color-scheme: dark) {
    /* Surface-2 in dark is darker than the card, so the tinted strips need to
       lift rather than recede — the light values were the other way round. */
    .m-al-restaurant,
    .m-al-session {
        background: var(--m-surface-2);
    }

    .m-al-pin-digit {
        background: var(--m-surface-2);
        border-color: var(--m-border);
    }

    /* Coral on a dark surface, matching the ruling: navy ink would be 1.14:1. */
    .m-al-security-note {
        background: rgba(236, 96, 126, 0.2);
        color: var(--m-coral);
    }
}

/* ---- responsive ---- */
@media (max-width: 480px) {
    .m-al-screen {
        padding: 0;
    }

    .m-al-card {
        max-width: 100%;
        min-height: 100vh;
        border-radius: var(--m-radius);
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .m-al-confirm,
    .m-al-success,
    .m-al-error,
    .m-al-expired {
        width: 100%;
    }

    .m-al-pin-digit {
        width: 40px;
        height: 48px;
        font-size: 1.1rem;
    }
}

/* ---- iOS safe areas ---- */
@supports (padding-top: env(safe-area-inset-top)) {
    .m-al-screen {
        padding-top: env(safe-area-inset-top);
        padding-bottom: env(safe-area-inset-bottom);
    }
}

/* =============================================================================
   SHARED MESERO COMPONENTS (#54 Phase A)
   =============================================================================
   The vocabulary the last three pages will consume. Defined BEFORE those pages
   migrate, on purpose: each page keeps its own inline copy until it moves, and an
   inline <style> wins on source order, so nothing below renders yet. Phase A is a
   no-op you can land and validate as such.

   WHICH NAMES BELONG HERE, AND WHY IT IS SEVEN AND NOT TWELVE

   Twelve class names are defined by two or more of MobileTabDetail, MobileOrders
   and MobileNewOrder. A first pass compared them by which PROPERTIES each version
   set and concluded seven should unify. That measure was wrong: it counts whether
   both versions set `color`, not whether they set it to the same thing. Re-measured
   on VALUE agreement:

       .header-title   100%   .btn-icon    93%   .error-state 75%   .btn-back  75%   -> unify
       .loading-state   56%   .btn-action  54%   .spinner     43%              -> merge, below
       .btn-retry       33%   .tabs-list   33%   .order-header 22%
       .item-price      20%   .item-name    0%                                 -> NOT shared

   The bottom five are different components that collided on a generic name. They
   get page-local `m-<page>-*` names in Phase B and never appear here. `.item-name`
   at 0% is the case the guide already calls out under M-0.1; `.item-price` at 20%
   is the same story — a small muted secondary price in one page, a bold coral
   primary price in the other.
   ============================================================================= */

/* ---- Identical in every page that defines it. Verbatim. ---- */
.m-header-title {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    /* #262 — every line in here is one line, ellipsised, so `overflow: hidden` clips it to exactly one line
       box. At 1.15 that box was shorter than Montserrat itself (1.219em from ascender to descender, in the
       font's hhea table), and the clip cut the tail off every g, j, p, q and y — "Diego Navarro" under
       "Mesa B1". 1.25 clears the face at every size used here, and the block still fits beside the 44px
       header controls. */
    line-height: 1.25;
}

/* #262 — the table name too. Bootstrap sets h1 to 1.2, which is just as short of the face. */
.m-header-title h1 {
    line-height: inherit;
}

/* 13 of 14 declarations identical; TabDetail additionally set `flex-shrink: 0`,
   which is kept — it stops the button collapsing in a tight header row and is
   inert where it was not set.

   #235 — .m-btn-back joins it rather than copying it, and its base and hover rules now live
   ONLY here. Before, the back button was styled in four places: its own near-duplicate
   block, a standalone :hover further down, a share of `.m-no-btn-cart`'s rule that sat later
   in the file and quietly won, and ShiftEnd's separate `.m-se-btn-back` copy — which had lost
   the padding, the background-clip, the centring and the transition, so it painted its fill
   across the whole 44px target where the others inset it to 36px. The two declarations that
   did not come across were never the back button's: `font-size: 1rem` sized MobileNewOrder's
   literal "←", and `position: relative` anchors the CART button's absolutely positioned badge. */
.m-btn-icon,
.m-btn-back {
    width: var(--m-touch-min);
    height: var(--m-touch-min);
    padding: 4px;
    border: none;
    border-radius: var(--m-radius);
    background: rgba(255, 255, 255, 0.1);
    background-clip: content-box;
    color: var(--m-on-chrome);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: background 0.15s ease;
}

/* 6 of 8 agreed. The two that did not:
     padding  2rem (TabDetail, NewOrder) vs `2rem 1rem` (Orders) -> 2rem, the majority
     color    only Orders set it, as var(--text-secondary) — one of the shadowed
              app.css globals. Taken as --m-fg-muted, which is what it meant. */
.m-error-state {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    text-align: center;
    color: var(--m-fg-muted);
}

/* ---- Merged, with the differences decided rather than averaged ---- */

/* All three agree on the flex column; they differ on padding and colour only.
   Same treatment as .m-error-state, which is its sibling — the two occupy the same
   slot and swap, so they must match. */
.m-loading-state {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    text-align: center;
    color: var(--m-fg-muted);
}

/* THREE DECISIONS, none of them a compromise between the two versions:

   size        40px (TabDetail, Orders) over NewOrder's 36px — majority, and the
               larger reads better as a full-screen loading indicator.
   track       #e5e7eb -> --m-border.
   head        **this is a defect fix, not a merge.** TabDetail and Orders set
               border-top-color: #3b82f6 — a blue that appears in NO Mesero palette,
               admin's or otherwise. NewOrder used coral. Neither wins: it takes
               --m-brand, the emphasis token, which is navy in light and coral in
               dark per the 2026-08-08 ruling.
   margin      TabDetail's `margin-bottom: 1rem` is NOT included; spacing belongs to
               the container, and .m-loading-state already has the padding.

   Consumes the existing `m-spin` keyframe rather than declaring another. Duration
   stays on the animation shorthand, so 1s here and 0.8s elsewhere coexist. */
.m-spinner {
    width: 40px;
    height: 40px;
    border: 3px solid var(--m-border);
    border-top-color: var(--m-brand);
    border-radius: 50%;
    animation: m-spin 1s linear infinite;
}

/* 7 of 13 agreed — the flex centring, gap, borderless fill, weight and cursor. The
   six that did not are all SIZING: TabDetail is a compact in-row action
   (flex: 1, .75rem .5rem, .8125rem) and NewOrder is a full-width sheet button
   (.875rem 1.5rem, 1rem). That is a real difference and averaging it would make
   both wrong, so only the shared identity lives here; each page adds its own
   padding, font-size and flex behaviour as a modifier in Phase B.

   min-height and border-radius are added rather than merged: both pages are on the
   Mesero surface, which is held to a 44px touch floor and square edges. TabDetail
   already declared both; NewOrder simply never did. */
.m-btn-action {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: var(--m-touch-min);
    border: none;
    border-radius: var(--m-radius);
    font-weight: 600;
    cursor: pointer;
}

/* ---- Shared keyframes ----
   `spin` is defined by all three pages and `slideUp` by two. Keyframe names are
   scoped by nothing — not by isolation, not by a class prefix — so they collide the
   moment two pages share this sheet. `m-spin` already exists above; this is the
   other one. `fadeIn` and `mobileToastIn` are single-page and get namespaced with
   their page in Phase B. */
@keyframes m-slide-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

/* ---- Orders screen (/m/orders) ------------------------------------------------
   Migrated from MobileOrders.razor (#54 Phase B). 930 inline lines.

   Consumes the Phase A vocabulary: .m-btn-icon, .m-error-state, .m-loading-state and
   .m-spinner are gone from here entirely — this page's copies were four of the seven
   that unified. Only their descendant and modifier rules survive, renamed.

   NAVY SPLIT BY ROLE, which is the one judgement in this migration. A navy BACKGROUND
   is chrome and does not swap (.m-mo-mobile-header, .m-mo-fab); a navy FOREGROUND is
   emphasis and becomes coral in dark per the 2026-08-08 ruling. That fixes three live
   defects — .tc-scheduled-chip, .stat-icon and .pickup-banner i had navy text with NO
   dark override, rendering ~1.14:1 against the dark surface. Three others
   (.tc-table, .tc-bill-chip, .shift-time) already had overrides and are unaffected,
   since those still win on source order.

   The three shadowing declarations are gone: --bg-primary, --text-primary and
   --text-secondary were declared in this page's dark block over names app.css defines
   globally with DIFFERENT values. They map to --m-bg / --m-fg / --m-fg-muted.

   Keyframes: spin and slideUp were page-local copies of shared animations and are
   dropped for m-spin / m-slide-up; fadeIn is page-local and becomes m-mo-fade-in. */

.m-mo-mobile-orders-container.m-mo-hidden {
    visibility: hidden;
}

.m-mo-mobile-orders-container {
    height: 100vh;
    height: 100dvh;
    background: var(--m-bg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    overscroll-behavior: none;
    padding-bottom: 72px;
    font-variant-numeric: tabular-nums;
}

.m-mo-mobile-header {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.625rem 0.75rem;
    background: var(--m-chrome);
    color: white;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.m-mo-header-left {
    display: flex;
    align-items: center;
    min-width: 0;
}

.m-mo-waiter-info {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.m-mo-waiter-avatar {
    width: 32px;
    height: 32px;
    border-radius: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.8125rem;
    border-bottom: 2px solid var(--m-coral);
    flex-shrink: 0;
}

.m-mo-waiter-details {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.m-mo-waiter-name {
    font-weight: 600;
    font-size: 0.9375rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-mo-restaurant-name {
    font-size: 0.6875rem;
    color: rgba(255, 255, 255, 0.85);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-mo-header-right {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex-shrink: 0;
}

.m-btn-icon:hover,
.m-btn-back:hover {
    background: rgba(255, 255, 255, 0.2);
}

/* #235 — the back arrows were never in this selector, so they rendered at the button's
   inherited 1rem: 14% larger than the bell, refresh and kebab beside them in the same
   header. Same row, same target size, two glyph sizes.

   The cart joins them, or the mismatch would just move one button to the right: it shares
   MobileNewOrder's header with the back arrow and inherits 1rem from its own rule. */
.m-btn-icon > i,
.m-btn-back > i,
.m-no-btn-cart > i {
    font-size: 0.875rem;
}

.m-mo-session-warning {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    background: #fef3c7;
    color: #92400e;
    font-size: 0.75rem;
}

.m-mo-connection-warning {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    background: rgba(236, 96, 126, 0.12);
    color: var(--m-coral);
    font-size: 0.75rem;
    font-weight: 600;
}

.m-mo-btn-retry-inline {
    margin-left: auto;
    min-height: 32px;
    padding: 0.25rem 0.75rem;
    background: transparent;
    color: inherit;
    border: 1px solid currentColor;
    border-radius: 0;
    font-weight: 600;
    cursor: pointer;
}

.m-error-state i {
    font-size: 2.5rem;
    color: var(--m-coral);
    margin-bottom: 0.5rem;
}

.m-mo-btn-retry {
    margin-top: 0.75rem;
    min-height: 44px;
    padding: 0.5rem 1rem;
    background: var(--m-chrome);
    color: #fff;
    border: none;
    border-radius: 0;
    font-weight: 600;
    cursor: pointer;
}

.m-mo-session-expired {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2rem;
}

.m-mo-expired-icon {
    width: 80px;
    height: 80px;
    background: #fef3c7;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 1.5rem;
}

.m-mo-expired-icon i {
    font-size: 2.5rem;
    color: #d97706;
}

.m-mo-session-expired h2 {
    margin: 0 0 0.5rem;
    color: var(--m-fg);
}

.m-mo-session-expired p {
    margin: 0;
    color: var(--m-fg-muted);
    line-height: 1.5;
}

.m-mo-tab-filters {
    position: relative;
    display: flex;
    gap: 0;
    padding: 0;
    background: white;
    border-bottom: 1px solid #e5e7eb;
    overflow: hidden;
}

.m-mo-filter-btn {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0.5rem 0.25rem;
    border: none;
    border-radius: 0;
    background: white;
    cursor: pointer;
    transition: color 0.2s ease;
    min-width: 0;
    position: relative;
    z-index: 1;
}

.m-mo-filter-btn.m-mo-active .m-mo-filter-count {
    color: var(--m-brand);
}

.m-mo-filter-btn.m-mo-active .m-mo-filter-label {
    color: var(--m-brand);
    font-weight: 700;
}

.m-mo-filter-count {
    font-size: 1rem;
    font-weight: 700;
    color: var(--m-fg);
    transition: color 0.2s ease;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.m-mo-filter-label {
    font-size: 0.6875rem;
    color: var(--m-fg);
    text-transform: uppercase;
    letter-spacing: 0.02em;
    transition: color 0.2s ease, font-weight 0.2s ease;
}

.m-mo-filter-indicator {
    position: absolute;
    bottom: 0;
    left: 0;
    width: calc(100% / 3);
    height: 3px;
    background: var(--m-brand);
    transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
    z-index: 2;
}

.m-mo-swipe-viewport {
    flex: 1;
    overflow: hidden;
    position: relative;
    min-height: 0;
}

.m-mo-swipe-track {
    display: flex;
    height: 100%;
    will-change: transform;
    transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.m-mo-swipe-panel {
    flex: 0 0 100%;
    width: 100%;
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-y: contain;
}

.m-mo-tabs-list {
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    min-height: 100%;
}

.m-mo-tab-card {
    background: white;
    border-radius: 0;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    border-left: 3px solid var(--m-coral);
    padding: 0.625rem 0.75rem 0.625rem 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.m-mo-tab-card:active {
    transform: scale(0.99);
    box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}

.m-mo-tab-card.m-mo-paid {
    opacity: 0.65;
    border-left-color: #9ca3af;
}

/* A settled card is dimmed to recede -- but an abandoned write-off carries m-mo-paid too
   (CloseTabCommand sets IsPaid on every close), so the amber "Abandonada" pill was being dimmed
   along with it. That pill exists precisely to catch the waiter's eye on a tab where NOTHING was
   collected, so dimming it works against the one card that most needs reading.

   Opacity composites the whole subtree against the page, dragging both the pill's ink and the
   card's ground toward the background and compressing the contrast between them -- which is also
   why the CssContract measurement of that pill could only ever be the undimmed case. Exempting
   the card makes the measured ratio the real one. */
.m-mo-tab-card.m-mo-paid.m-mo-not-dimmed {
    opacity: 1;
}

.m-mo-tc-row1 {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    min-width: 0;
}

.m-mo-tc-table {
    font-weight: 700;
    font-size: 0.875rem;
    color: var(--m-brand);
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-customer {
    flex: 1;
    min-width: 0;
    font-size: 0.8125rem;
    color: var(--m-fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-mo-tc-time {
    font-size: 0.6875rem;
    color: var(--m-fg-muted);
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-row2 {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
}

.m-mo-tc-stat {
    color: var(--m-fg);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-stat-label {
    color: var(--m-fg-muted);
    font-weight: 500;
    font-size: 0.75rem;
}

.m-mo-tc-total {
    color: var(--m-brand);
    font-weight: 700;
}

.m-mo-tc-dot {
    color: #d1d5db;
    font-weight: 400;
}

.m-mo-tc-spacer {
    flex: 1;
}

.m-mo-tc-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.01em;
}

.m-mo-tc-pill::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.m-mo-tc-pill.m-mo-pending {
    color: #b45309;
}

.m-mo-tc-pill.m-mo-queued {
    color: #6f42c1;
}

.m-mo-tc-pill.m-mo-ready {
    color: #0c5460;
}

.m-mo-tc-pill.m-mo-delivered {
    color: #065f46;
}

.m-mo-tc-pill.m-mo-paid {
    color: #1e40af;
}

/* Abandoned write-off: amber, matching .m-mo-tc-subpill.m-mo-partial-pay's ramp rather than the
   blue "Pagado" it replaces. A waiter reading their own tab list must not see a written-off tab
   in the same colour as a collected one. */
.m-mo-tc-pill.m-mo-abandoned {
    color: #b45309;
}

.m-mo-tc-chev {
    color: #d1d5db;
    font-size: 0.6875rem;
    margin-left: 0.125rem;
}

.m-mo-tc-cash-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 1px 6px;
    font-size: 0.6875rem;
    font-weight: 700;
    background: var(--m-coral);
    color: var(--m-chrome);
    margin-left: 0.25rem;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-cash-badge i {
    font-size: 0.625rem;
}

.m-mo-tc-silence-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 1px 6px;
    font-size: 0.625rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: rgba(107, 114, 128, 0.10);
    color: #6b7280;
    border: 1px solid rgba(107, 114, 128, 0.30);
    margin-left: 0.25rem;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-silence-chip i {
    font-size: 0.5625rem;
    opacity: 0.8;
}

.m-mo-tc-scheduled-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 1px 6px;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    background: rgba(0, 0, 117, 0.10);
    color: var(--m-brand);
    border: 1px solid rgba(0, 0, 117, 0.30);
    margin-left: 0.25rem;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-scheduled-chip i {
    font-size: 0.5625rem;
    opacity: 0.85;
}

.m-mo-tc-bill-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 1px 6px;
    font-size: 0.625rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: rgba(0, 0, 117, 0.08);
    color: var(--m-brand);
    border: 1px solid rgba(0, 0, 117, 0.30);
    margin-left: 0.25rem;
    font-variant-numeric: tabular-nums;
}

.m-mo-tc-bill-chip i {
    font-size: 0.5625rem;
    opacity: 0.85;
}

/* #204 — the duration inside an uppercased chip stays as written: "12 min", never "12 MIN". */
.m-mo-tc-silence-chip .m-mo-tc-dur,
.m-mo-tc-bill-chip .m-mo-tc-dur {
    text-transform: none;
    letter-spacing: 0;
}

/* #207 — status and duration wrap as one text block beside the icon. The separator is
   gone from the markup rather than hidden: a "·" that survives the wrap strands itself at
   the head of the second line, which is exactly what the card reported. Wrapping here (not
   on the chip) keeps the duration left-aligned under the status word instead of pushed to
   the chip's far edge, and a chip that still fits stays on one line.

   m-* rather than m-mo-*: the orders card and the tab-detail header carry the same chip,
   and the second one wraps too once a duration passes the hour. */
.m-chip-text {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.25rem;
    row-gap: 0;
    min-width: 0;
}

.m-mo-tc-bill-chip.m-mo-urgent {
    background: var(--m-warn-bg);
    color: #92400e;
    border-color: var(--m-warn);
}

.m-mo-tc-subpill.m-mo-partial-pay {
    display: inline-flex;
    align-items: center;
    padding: 1px 6px;
    font-size: 0.625rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: rgba(245, 158, 11, 0.12);
    color: #b45309;
    border: 1px solid rgba(245, 158, 11, 0.45);
    margin-left: 0.25rem;
}

.m-mo-pickup-banner {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: rgba(0, 0, 117, 0.04);
    border-left: 3px solid var(--m-chrome);
    margin-bottom: 0.5rem;
    font-family: 'Montserrat', sans-serif;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--m-fg);
}

.m-mo-pickup-banner i {
    color: var(--m-brand);
    font-size: 0.875rem;
}

.m-mo-empty-state {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 3rem 2rem;
    color: var(--m-fg-muted);
}

.m-mo-empty-state i {
    font-size: 3rem;
    color: #d1d5db;
    margin-bottom: 1rem;
}

.m-mo-empty-state h3 {
    margin: 0 0 0.5rem;
    color: var(--m-fg);
}

.m-mo-empty-state p {
    margin: 0;
}

.m-mo-empty-state .m-mo-coming-soon {
    font-size: 0.85rem;
    color: #9ca3af;
    margin-top: 0.5rem;
}

.m-mo-fab {
    position: fixed;
    bottom: 5rem;
    right: 1.5rem;
    width: 52px;
    height: 52px;
    border-radius: 0;
    border: none;
    background: var(--m-chrome);
    color: white;
    font-size: 1.375rem;
    box-shadow: 0 4px 12px rgba(0, 0, 117, 0.35);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.15s ease, box-shadow 0.15s ease, bottom 0.25s cubic-bezier(0.16, 1, 0.3, 1);
    z-index: 100;
}

.m-mo-fab.m-mo-fab--shifted {
    bottom: 14rem;
}

.m-mo-fab.m-mo-fab--above-calls-sheet {
    bottom: calc(var(--m-calls-sheet-h, 80vh) + 1rem);
    z-index: 260;
    transition: bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.m-mo-fab:active {
    transform: scale(0.94);
}

.m-mo-menu-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 200;
    display: flex;
    align-items: flex-end;
    animation: m-mo-fade-in 0.2s ease;
}

@keyframes m-mo-fade-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

.m-mo-menu-sheet {
    width: 100%;
    background: white;
    border-radius: 0;
    animation: m-slide-up 0.3s ease;
}

.m-mo-menu-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid #e5e7eb;
}

.m-mo-menu-header h3 {
    margin: 0;
    font-size: 1.1rem;
}

.m-mo-btn-close-menu {
    width: 32px;
    height: 32px;
    border-radius: 0;
    border: none;
    background: #f3f4f6;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.m-mo-menu-items {
    padding: 0.5rem 0;
}

.m-mo-menu-item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 1rem;
    color: var(--m-fg);
    text-align: left;
}

.m-mo-menu-item:active {
    background: #f3f4f6;
}

.m-mo-menu-item i {
    width: 24px;
    text-align: center;
    color: #6b7280;
}

.m-mo-menu-item.m-mo-logout {
    color: #dc2626;
}

.m-mo-menu-item.m-mo-logout i {
    color: #dc2626;
}

.m-mo-menu-divider {
    height: 1px;
    background: #e5e7eb;
    margin: 0.5rem 0;
}

.m-mo-shift-summary-sheet {
    width: 100%;
    background: white;
    border-radius: 0;
    animation: m-slide-up 0.3s ease;
    max-height: 85vh;
    overflow-y: auto;
}

.m-mo-shift-summary-sheet .m-mo-menu-header h3 {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.m-mo-shift-summary-sheet .m-mo-menu-header h3 i {
    color: var(--m-coral);
}

.m-mo-shift-summary-content {
    padding: 1.25rem;
}

.m-mo-summary-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.m-mo-summary-stat {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem;
    background: #f8fafc;
    border-radius: 0;
}

.m-mo-stat-icon {
    width: 44px;
    height: 44px;
    /* #218 follow-up — the box may not shrink. It is a flex ITEM of .m-mo-summary-stat,
       whose automatic minimum size is its content: a wide glyph (fa-list-ol) could push
       the square wider than a narrow one (fa-clock) even though both declare 44px. */
    flex: 0 0 44px;
    box-sizing: border-box;
    border-radius: 0;
    background: rgba(0, 0, 117, 0.1);
    color: var(--m-brand);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
}

/* #218 follow-up — the SQUARES were never the problem. width/height: 44px is declared in
   exactly one place and no media query overrides it, so all four boxes always agreed.
   What differed is what sits inside them: fa-receipt, fa-utensils, fa-clock and fa-list-ol
   carry different intrinsic advance widths and ink heights, and the <i> had no box of its
   own — so four identical squares held four visibly different icons and the row stopped
   reading as a set. 1.25em is Font Awesome's own fixed-width convention; centring the
   glyph in it makes the four optically equal without touching the squares. */
.m-mo-stat-icon > i {
    flex: 0 0 auto;
    width: 1.25em;
    height: 1.25em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.m-mo-stat-icon.m-mo-active {
    background: #d1fae5;
    color: #059669;
}

.m-mo-stat-icon.m-mo-pending {
    background: #fef3c7;
    color: #d97706;
}

.m-mo-stat-icon.m-mo-orders {
    background: rgba(236, 96, 126, 0.15);
    color: var(--m-coral);
}

.m-mo-stat-details {
    display: flex;
    flex-direction: column;
}

.m-mo-stat-details .m-mo-stat-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--m-fg);
    line-height: 1.2;
}

.m-mo-stat-details .m-mo-stat-label {
    font-size: 0.75rem;
    color: var(--m-fg-muted);
}

.m-mo-summary-totals {
    background: #f8fafc;
    border-radius: 0;
    padding: 1rem;
    margin-bottom: 1rem;
}

.m-mo-total-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem 0;
}

.m-mo-total-row:not(:last-child) {
    border-bottom: 1px solid #e5e7eb;
}

.m-mo-total-label {
    color: var(--m-fg-muted);
}

.m-mo-total-value {
    font-weight: 600;
    color: var(--m-fg);
}

.m-mo-total-row.m-mo-highlight {
    padding-top: 0.75rem;
}

.m-mo-total-row.m-mo-highlight .m-mo-total-label {
    font-weight: 600;
    color: var(--m-fg);
}

.m-mo-total-row.m-mo-highlight .m-mo-total-value {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--m-coral);
}

.m-mo-shift-time {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.75rem;
    background: rgba(0, 0, 117, 0.1);
    border-radius: 0;
    font-size: 0.9rem;
    color: var(--m-brand);
}

@media (prefers-color-scheme: dark) {
    .m-mo-tab-filters {
        background: #1e293b;
        border-bottom-color: #334155;
    }

    .m-mo-filter-btn {
        background: #1e293b;
    }

    .m-mo-filter-count {
        color: #f9fafb;
    }

    .m-mo-tab-card {
        background: #1e293b;
    }

    .m-mo-tc-table {
        color: #e0e7ff;
    }

    .m-mo-tc-customer {
        color: #f9fafb;
    }

    .m-mo-tc-stat {
        color: #f9fafb;
    }

    .m-mo-tc-total {
        color: var(--m-brand);
    }

    .m-mo-tc-stat-label {
        color: #94a3b8;
    }

    .m-mo-tc-dot {
        color: #475569;
    }

    .m-mo-tc-chev {
        color: #475569;
    }

    .m-mo-tc-pill.m-mo-pending {
        color: #fbbf24;
    }

    .m-mo-tc-pill.m-mo-queued {
        color: #c4b5fd;
    }

    .m-mo-tc-pill.m-mo-ready {
        color: #7dd3fc;
    }

    .m-mo-tc-pill.m-mo-delivered {
        color: #34d399;
    }

    .m-mo-tc-pill.m-mo-paid {
        color: #93c5fd;
    }

    .m-mo-tc-pill.m-mo-abandoned {
        color: #fbbf24;
    }

    .m-mo-tc-subpill.m-mo-partial-pay {
        background: rgba(245, 158, 11, 0.18);
        color: #fcd34d;
        border-color: rgba(245, 158, 11, 0.45);
    }

    .m-mo-tc-silence-chip {
        background: rgba(255, 255, 255, 0.06);
        color: #9ca3af;
        border-color: rgba(255, 255, 255, 0.18);
    }

    .m-mo-tc-bill-chip {
        background: rgba(255, 255, 255, 0.08);
        color: #c0c2c8;
        border-color: rgba(255, 255, 255, 0.20);
    }

    /* #219 — the urgent (Estancado) variant had no dark rule at all, so its light-mode #92400e
       stayed on the amber tint: about 1.9:1. Mirrors the tab-detail chip, which already swaps. */
    .m-mo-tc-bill-chip.m-mo-urgent {
        background: rgba(245, 158, 11, 0.18);
        color: #fbbf24;
        border-color: rgba(245, 158, 11, 0.45);
    }

    .m-mo-menu-sheet {
        background: #1e293b;
    }

    .m-mo-menu-header {
        border-bottom-color: #334155;
    }

    .m-mo-btn-close-menu {
        background: #334155;
        color: white;
    }

    .m-mo-menu-divider {
        background: #334155;
    }

    .m-mo-menu-item:active {
        background: #334155;
    }

    .m-mo-expired-icon {
        background: rgba(217, 119, 6, 0.2);
    }

    .m-mo-shift-summary-sheet {
        background: #1e293b;
    }

    .m-mo-summary-stat {
        background: #0f172a;
    }

    /* #218 — all four tiles, one family. The card reports the bare tile having no square at all:
       its rgba(0,0,117,0.1) is navy on slate, invisible against the panel. But fixing only that
       leaves four squares in four different treatments — two bright pastels carried over from
       light (mint, amber) beside two translucent ones — which is the same inconsistency wearing
       a different face. In dark every tile is a translucent tint of its own hue with a light
       glyph, so the set reads as one row of four. */
    .m-mo-stat-icon {
        background: rgba(148, 163, 184, 0.18);
        color: #cbd5e1;
    }

    .m-mo-stat-icon.m-mo-active {
        background: rgba(16, 185, 129, 0.18);
        color: #34d399;
    }

    .m-mo-stat-icon.m-mo-pending {
        background: rgba(245, 158, 11, 0.18);
        color: #fbbf24;
    }

    .m-mo-stat-icon.m-mo-orders {
        background: rgba(236, 96, 126, 0.18);
        color: var(--m-coral);
    }

    .m-mo-summary-totals {
        background: #0f172a;
    }

    .m-mo-total-row:not(:last-child) {
        border-bottom-color: #334155;
    }

    .m-mo-shift-time {
        background: rgba(236, 96, 126, 0.2);
        color: var(--m-coral);
    }
}

@supports (padding-top: env(safe-area-inset-top)) {
    .m-mo-mobile-header {
        padding-top: calc(0.625rem + env(safe-area-inset-top));
    }

    .m-mo-fab {
        bottom: calc(5rem + env(safe-area-inset-bottom));
    }

    .m-mo-fab.m-mo-fab--shifted {
        bottom: calc(14rem + env(safe-area-inset-bottom));
    }

    .m-mo-fab.m-mo-fab--above-calls-sheet {
        bottom: calc(80vh + 1rem + env(safe-area-inset-bottom));
    }
}

/* ---- New-order screen (/m/new-order) -------------------------------------------
   Migrated from MobileNewOrder.razor (#54 Phase B). 753 inline lines, 100 classes —
   the largest class count of the seven pages.

   Six Phase A names lived here: .btn-back .btn-action .error-state .header-title
   .loading-state .spinner. Their base rules are gone; descendants and modifiers stay,
   renamed. Four generics that LOOK shared are not — .item-name (0% value agreement
   with TabDetail's), .item-price (20%), .order-header (22%) and .tabs-list (33%) are
   different components that collided on a name, so they take page-local m-no- names.

   Navy split by role as elsewhere: six backgrounds and borders take --m-chrome; the
   single foreground (.mobile-toast--info's icon) takes --m-brand.

   --pt-font-body is deliberately left as-is. It is a font family, not a palette
   value, and the Mesero vocabulary has no font token to move it onto — inventing one
   to avoid a --pt- reference would be worse than the reference.

   This page declared NO custom properties, so there was no shadowing to unwind. */

/* REMOVED — a dead rule carried over honestly and then deleted deliberately.

   MobileNewOrder's inline block contained:

       :global(html), :global(body) {
           overscroll-behavior: none; overflow: hidden;
           position: fixed; width: 100%; height: 100%;
       }

   `:global()` is Blazor CSS-ISOLATION syntax, meaningful only inside a
   Component.razor.css that the build rewrites. This lived in an inline <style>,
   which Blazor emits into the DOM verbatim, so the browser saw an unknown
   pseudo-class, rejected the selector and dropped the whole rule. **It has never
   done anything** — the scroll lock it was written for was never in effect.

   It is not carried forward, and it must not be "fixed" by dropping the :global()
   wrapper. mesero.css is a real global stylesheet: `html, body { position: fixed }`
   here would apply to the ENTIRE app, freezing scrolling on every admin page. If
   the new-order screen genuinely needs a scroll lock, it wants a body class the
   page toggles on mount and removes on dispose, not a rule in a shared sheet.
   Raised on #54. */

.m-no-mobile-new-order {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow: hidden;
    background: #f8fafc;
    display: flex;
    flex-direction: column;
    max-width: 100vw;
    font-variant-numeric: tabular-nums;
}

.m-no-order-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.625rem;
    background: var(--m-chrome);
    color: white;
    flex-shrink: 0;
}

/* #235 — the back button used to share this rule, which is why merging it into
   .m-btn-icon above did nothing on its own: this block is ~1270 lines later and wins at
   equal specificity, re-supplying the very declarations that merge removed. It keeps
   `position: relative` because .m-no-cart-badge below is absolutely positioned against
   it — that declaration was always the CART's, not the back arrow's. */
.m-no-btn-cart {
    width: 44px;
    height: 44px;
    padding: 4px;
    border: none;
    background: rgba(255, 255, 255, 0.1);
    background-clip: content-box;
    color: white;
    font-size: 1rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    transition: background 0.15s ease;
}

.m-no-btn-cart:hover {
    background: rgba(255, 255, 255, 0.2);
}

.m-header-title h1 {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-no-header-subtitle {
    font-size: 0.6875rem;
    opacity: 0.85;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-no-cart-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    background: var(--m-coral);
    color: var(--m-chrome);
    font-size: 0.625rem;
    font-weight: 700;
    min-width: 16px;
    height: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--m-chrome);
    font-variant-numeric: tabular-nums;
}

.m-no-spinner-small {
    width: 20px;
    height: 20px;
    border: 2px solid #e5e7eb;
    border-top-color: var(--m-coral);
    border-radius: 50%;
    animation: m-spin 1s linear infinite;
}

.m-no-loading-inline {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem;
    color: #6b7280;
}

.m-no-step-container {
    flex: 1;
    overflow-y: auto;
    padding: 1rem;
    -webkit-overflow-scrolling: touch;
}

.m-no-step-header {
    margin-bottom: 1.25rem;
}

.m-no-step-header h2 {
    margin: 0 0 0.25rem;
    font-size: 1.25rem;
    color: #1f2937;
}

.m-no-step-header p {
    margin: 0;
    font-size: 0.85rem;
    color: #6b7280;
}

.m-no-tabs-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

.m-no-tab-option {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.875rem 1rem;
    background: white;
    border: 2px solid #e5e7eb;
    border-left: 4px solid var(--m-coral);
    cursor: pointer;
    text-align: left;
}

.m-no-tab-option:active {
    background: var(--m-surface-2);
}

.m-no-tab-option-main {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.m-no-tab-table {
    font-weight: 600;
    color: #1f2937;
}

.m-no-tab-customer {
    font-size: 0.85rem;
    color: #6b7280;
}

.m-no-tab-option-meta {
    text-align: right;
    font-size: 0.8rem;
    color: #6b7280;
}

.m-no-tab-total {
    display: block;
    font-weight: 600;
    color: var(--m-coral);
}

.m-no-empty-tabs {
    padding: 2rem;
    text-align: center;
    color: #9ca3af;
    background: #f9fafb;
    margin-bottom: 1rem;
}

.m-no-btn-new-tab {
    width: 100%;
    padding: 1rem;
    background: var(--m-chrome);
    color: white;
    border: none;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
}

.m-no-form-section {
    margin-bottom: 1rem;
}

.m-no-form-label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: #374151;
    margin-bottom: 0.35rem;
}

.m-no-required {
    color: #dc2626;
}

.m-no-form-select,
.m-no-form-input {
    width: 100%;
    padding: 0.75rem;
    font-size: 1rem;
    border: 2px solid #e5e7eb;
    background: white;
    color: #1f2937;
}

/* #214 — the table trigger reads as the field it replaces (same box, same border, same
   height), because it IS the field: tapping it opens the drawer instead of the OS picker. */
.m-no-table-trigger {
    width: 100%;
    box-sizing: border-box;
    min-height: var(--m-touch-min);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.75rem;
    font-family: inherit;
    font-size: 1rem;
    text-align: left;
    border: 2px solid #e5e7eb;
    border-radius: 0;
    background: white;
    color: #1f2937;
    cursor: pointer;
}

.m-no-table-trigger.m-no-placeholder {
    color: var(--m-fg-muted);
}

.m-no-table-trigger > i {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: var(--m-fg-muted);
}

.m-no-table-section + .m-no-table-section {
    margin-top: 0.75rem;
}

.m-no-table-section-label {
    display: block;
    padding: 0.25rem 0;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--m-fg-muted);
}

/* Full-width rows, 44px floor: the point of the drawer is that a table is a thumb target
   rather than a line in a wheel. */
.m-no-table-option {
    width: 100%;
    box-sizing: border-box;
    min-height: var(--m-touch-min);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.75rem;
    margin-bottom: 0.375rem;
    font-family: inherit;
    font-size: 1rem;
    text-align: left;
    background: white;
    border: 2px solid #e5e7eb;
    border-left: 4px solid var(--m-coral);
    border-radius: 0;
    color: #1f2937;
    cursor: pointer;
}

.m-no-table-option.m-no-selected {
    border-color: var(--m-coral);
    background: var(--m-surface-2);
}

.m-no-table-option > i {
    color: var(--m-coral);
}

/* MC8 — "Mesas libres": nobody's tables. A row here takes the table rather than picking it, so it drops the
   coral edge that marks a table a tab can open on, and names its action on the right. Two classes, so the edge
   holds against the single-class coral one in the dark block below. */
.m-no-table-option.m-no-table-free {
    border-left-color: var(--m-hairline);
}

.m-no-table-option.m-no-table-free:disabled {
    cursor: progress;
    opacity: 0.6;
}

.m-no-table-free-section {
    font-weight: 400;
    color: var(--m-fg-muted);
}

/* --m-brand: navy on the white row, coral on the dark one (4.56:1). Never coral ink on white (PG-c). */
.m-no-table-take {
    flex: 0 0 auto;
    font-size: 0.875rem;
    font-weight: 700;
    color: var(--m-brand);
}

.m-no-form-select:focus,
.m-no-form-input:focus {
    outline: none;
    border-color: var(--m-coral);
}

.m-no-info-banner {
    padding: 0.75rem;
    background: #fef3c7;
    color: #92400e;
    font-size: 0.85rem;
    margin-bottom: 1rem;
}

.m-no-btn-primary {
    background: var(--m-chrome);
    color: white;
    /* #350 — an edge in its OWN colour, so it is invisible and the box still matches the outlined
       secondary below it. `.m-btn-action` gives this button `border: none`, and the secondary carries a
       2px edge, so the pair differed by 4px of border before any padding was counted. Matching the box
       rather than deleting the secondary's edge keeps the outline that makes it read as secondary, and
       it holds under either box-sizing model — mesero.css sets border-box per rule, not globally. */
    border: 2px solid var(--m-chrome);
    font-size: 1rem;
}

/* #205 — "Guardar para después": secondary to Continuar al Menú, same footprint.
   #350 — it now IS the same footprint. This rule used to restate the whole layout — display, width,
   min-height, padding, radius, weight, cursor — instead of sharing `.m-btn-action`, and the restatement
   drifted: `padding: 0.75rem 1rem` on top of a 2px edge put it at 52px against the primary's 44px, which
   is exactly the 44px floor. Cam reported the gap; the comment above had claimed parity since #205.
   #215 fixed the WIDTH of this same pair and stopped there.
   Reduced to what actually differs — the skin. */
.m-no-btn-secondary {
    margin-top: 0.5rem;
    background: white;
    color: var(--m-chrome);
    border: 2px solid var(--m-chrome);
    font-size: 1rem;
}

.m-no-btn-secondary:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.m-no-form-optional {
    font-weight: 400;
    color: var(--m-fg-muted);
}

/* #205 — names held for the picked table. */
.m-no-held-names {
    margin-bottom: 1rem;
}

.m-no-held-names-title {
    display: block;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--m-fg-muted);
    margin-bottom: 0.35rem;
}

.m-no-held-name {
    display: flex;
    align-items: stretch;
    background: white;
    border: 2px solid #e5e7eb;
    border-left: 4px solid var(--m-coral);
    margin-bottom: 0.5rem;
}

.m-no-held-name.m-no-selected {
    border-color: var(--m-coral);
    background: var(--m-surface-2);
}

.m-no-held-name-use {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    min-height: var(--m-touch-min);
    padding: 0.625rem 0.875rem;
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
}

.m-no-held-name-text {
    font-weight: 600;
    color: #1f2937;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.m-no-held-name-age {
    flex-shrink: 0;
    font-size: 0.75rem;
    color: var(--m-fg-muted);
}

.m-no-held-name-discard {
    width: var(--m-touch-min);
    flex-shrink: 0;
    background: transparent;
    border: none;
    border-left: 1px solid #e5e7eb;
    color: var(--m-fg-muted);
    cursor: pointer;
}

.m-no-btn-primary:disabled {
    background: #9ca3af;
    cursor: not-allowed;
}

.m-no-btn-full {
    width: 100%;
}

.m-btn-action.m-no-loading {
    background: #6b7280;
}

.m-no-menu-container {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    max-width: 100%;
}

/* Menu switcher (P5): a level ABOVE the category tabs, and styled to read that way — filled when
   active, where a category tab only tints. The two strips sit together, so the difference in weight
   is what says one selects within the other. */
.m-no-menu-switch {
    display: flex;
    gap: 0.375rem;
    padding: 0.5rem 0.625rem 0.375rem;
    overflow-x: auto;
    overflow-y: hidden;
    background: white;
    flex-shrink: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.m-no-menu-switch::-webkit-scrollbar {
    display: none;
}

.m-no-menu-chip {
    flex-shrink: 0;
    padding: 0.375rem 0.875rem;
    border: 1.5px solid #e5e7eb;
    border-radius: 999px;
    background: white;
    color: #6b7280;
    font-weight: 700;
    font-size: 0.8125rem;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.m-no-menu-chip.m-no-active {
    border-color: var(--m-coral);
    background: var(--m-coral);
    color: white;
}

.m-no-category-tabs {
    display: flex;
    gap: 0.375rem;
    padding: 0.5rem 0.625rem;
    overflow-x: auto;
    overflow-y: hidden;
    background: white;
    border-bottom: 1px solid #e5e7eb;
    flex-shrink: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.m-no-category-tabs::-webkit-scrollbar {
    display: none;
}

.m-no-category-tab {
    flex-shrink: 0;
    padding: 0.375rem 0.75rem;
    border: 1.5px solid #e5e7eb;
    background: white;
    font-weight: 600;
    font-size: 0.8125rem;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.m-no-category-tab.m-no-active {
    border-color: var(--m-coral);
    background: rgba(236, 96, 126, 0.08);
    color: var(--m-coral);
}

/* #145 - a ported menu fetches its items when first opened; this says so rather than showing an
   empty list, which reads as a menu with nothing on it. */
.m-no-menu-loading {
    padding: 1rem 0.75rem;
    color: #6b7280;
    font-size: 0.875rem;
    font-weight: 500;
    text-align: center;
}

/* Package rows on the tab detail. Mirrors TabDetailPanel's .tdp-* treatment, because the two render
   the same tab and a waiter moving between them should not have to notice which one they are on. */
.m-td-package-child {
    padding-left: 0.75rem;
}

.m-td-package-arrow {
    margin-right: 0.25rem;
    color: #9ca3af;
}

.m-td-package-chip {
    display: inline-block;
    margin-right: 0.375rem;
    padding: 0.0625rem 0.375rem;
    border-radius: 0.25rem;
    background: #eef2ff;
    color: #3730a3;
    font-size: 0.625rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
}

.m-td-child-count {
    margin-left: 0.25rem;
    color: #6b7280;
    font-weight: 600;
}

/* "Incluido" is a statement about the row, not a figure — italic and muted so it cannot be read as
   a second charge beside the header that already counts it. */
.m-td-included {
    font-size: 0.75rem;
    font-weight: 500;
    font-style: italic;
    color: #6b7280;
}

/* What an option normally costs, struck: muted, and never mistakable for the number charged. */
/* Per-unit context, never the charge: muted, and lighter than the contribution beside it. */
.m-no-option-price-unit {
    margin-left: 0.25rem;
    color: #6b7280;
    font-size: 0.75rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

.m-no-option-price-catalog {
    margin-left: 0.25rem;
    color: #9ca3af;
    font-size: 0.75rem;
    font-weight: 500;
    text-decoration: line-through;
    font-variant-numeric: tabular-nums;
}

.m-no-menu-items-list {
    flex: 1;
    padding: 0.5rem;
    padding-bottom: 88px;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}

.m-no-menu-item-card {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.625rem;
    background: white;
    margin-bottom: 0.375rem;
    border-left: 3px solid var(--m-coral);
    cursor: pointer;
    max-width: 100%;
    box-sizing: border-box;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    transition: transform 0.15s ease;
}

.m-no-menu-item-card:not(.m-no-sold-out):active {
    transform: scale(0.995);
    background: var(--m-surface-2);
}

.m-no-item-info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.m-no-item-name {
    font-weight: 600;
    color: #1f2937;
    font-size: 0.95rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.m-no-item-meta-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.m-no-item-price {
    flex-shrink: 0;
    font-weight: 700;
    color: var(--m-coral);
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
}

/* What the dish costs elsewhere, struck through beside the price on THIS menu. Muted and lighter
   than the live price: the comparison is context, and a waiter reading the row quickly must not
   mistake it for the number to charge. */
.m-no-item-price-catalog {
    flex-shrink: 0;
    font-weight: 500;
    font-size: 0.8rem;
    color: var(--m-text-secondary, #64748b);
    text-decoration: line-through;
    font-variant-numeric: tabular-nums;
}

.m-no-stock-pill {
    font-size: 0.6875rem;
    font-weight: 600;
    padding: 0.1rem 0.4rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.m-no-stock-pill.m-no-sold-out {
    background: rgba(236, 96, 126, 0.15);
    color: var(--m-coral);
}

.m-no-stock-pill.m-no-low-stock {
    background: #fef3c7;
    color: #92400e;
}

.m-no-menu-item-card.m-no-sold-out {
    opacity: 0.6;
    filter: grayscale(0.3);
    cursor: not-allowed;
    border-left-color: #9ca3af;
}

.m-no-btn-add-quick {
    width: 36px;
    height: 36px;
    background: var(--m-chrome);
    color: white;
    border: none;
    font-size: 1.25rem;
    cursor: pointer;
}

.m-no-btn-add-quick:disabled {
    background: #9ca3af;
    cursor: not-allowed;
    opacity: 0.6;
}

.m-no-cart-summary {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.625rem 0.75rem;
    background: var(--m-coral);
    color: var(--m-chrome);
    cursor: pointer;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.12);
}

.m-no-cart-info {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
}

.m-no-cart-count {
    font-size: 0.6875rem;
    opacity: 0.9;
}

.m-no-cart-total {
    font-size: 0.9375rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.m-no-cart-action {
    font-weight: 600;
    font-size: 0.8125rem;
}

.m-no-m-sheet-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(4px);
    z-index: 200;
    display: flex;
    align-items: flex-end;
}

.m-no-m-sheet {
    width: 100%;
    max-height: 85vh;
    background: white;
    display: flex;
    flex-direction: column;
    animation: m-slide-up 0.25s ease;
}

.m-no-m-sheet-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem;
    background: white;
    border-bottom: 1px solid #e5e7eb;
    flex-shrink: 0;
}

.m-no-m-sheet-header h2 {
    margin: 0;
    font-size: 1.1rem;
}

.m-no-m-sheet-close {
    width: 32px;
    height: 32px;
    background: #f3f4f6;
    border: none;
    cursor: pointer;
    font-size: 1rem;
}

.m-no-m-sheet-body {
    flex: 1;
    padding: 1rem;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.m-no-m-sheet-footer {
    padding: 1rem;
    border-top: 1px solid #e5e7eb;
    flex-shrink: 0;
}

.m-no-item-description {
    font-size: 0.9rem;
    color: #6b7280;
    margin: 0 0 0.75rem;
}

.m-no-item-price-display {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--m-coral);
    margin: 0 0 1rem;
}

.m-no-modifiers-section {
    margin-bottom: 1rem;
}

.m-no-modifier-group {
    margin-bottom: 1rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid #f3f4f6;
}

.m-no-modifier-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.m-no-modifier-name {
    font-weight: 600;
    color: #1f2937;
}

.m-no-modifier-badge {
    font-size: 0.65rem;
    padding: 0.15rem 0.4rem;
    font-weight: 600;
}

.m-no-modifier-badge.m-no-required {
    background: #fee2e2;
    color: #dc2626;
}

.m-no-modifier-badge.m-no-optional {
    background: #f3f4f6;
    color: #6b7280;
}

.m-no-modifier-options {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.m-no-modifier-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.6rem 0.75rem;
    border: 2px solid #e5e7eb;
    background: white;
    cursor: pointer;
    text-align: left;
}

.m-no-modifier-option.m-no-selected {
    border-color: var(--m-coral);
    background: rgba(236, 96, 126, 0.08);
}

/* #208 — the radio is drawn, not typed. "●" and "○" are different sizes in the same font,
   so the selected state rendered as a dot noticeably smaller than the empty circles above
   it. The glyph stays in the markup for assistive text but is not what you see: font-size:0
   hides it, the box draws the ring, and ::after draws the fill at a fixed 10px. */
.m-no-option-check {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    box-sizing: border-box;
    /* 4.78:1 on the white row. #9ca3af — the colour the old glyph carried — is 2.54:1,
       under the 3:1 floor a control boundary has to clear (WCAG 1.4.11). */
    border: 2px solid #6b7280;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0;
    line-height: 0;
    color: transparent;
}

.m-no-modifier-option.m-no-selected .m-no-option-check {
    border-color: var(--m-coral);
}

.m-no-modifier-option.m-no-selected .m-no-option-check::after {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--m-coral);
}

.m-no-option-name {
    flex: 1;
    color: #1f2937;
}

.m-no-option-price {
    color: var(--m-coral);
    font-weight: 600;
    font-size: 0.85rem;
}

/* #286 — "Personalizar", the way into a dish's modifiers when that dish sits inside a bundle.
   It was written in the markup and styled NOWHERE: no rule for this class existed, so it collapsed to its
   line box at 30px and OVERLAPPED the option row above it by 27px. Cam: "currently button is in a very
   narrow space". It reads as a caption wedged between two rows rather than the only way into the component's
   choices.

   Given a control's height and room of its own. Tokens rather than literals because --m-brand flips navy to
   coral in dark, so the affordance follows the scheme without a second rule. */
.m-no-component-customize {
    display: block;
    width: 100%;
    min-height: 44px;
    margin: 0.375rem 0;
    padding: 0.5rem 0.875rem;
    border: 1.5px solid var(--m-brand);
    border-radius: 0;
    background: var(--m-surface);
    color: var(--m-brand);
    font-weight: 700;
    font-size: 0.875rem;
    text-align: center;
    cursor: pointer;
}

.m-no-component-customize:active {
    background: var(--m-surface-2);
}

/* #286 — the way BACK out of a component detour, at the sheet's top-left. Same 44px target as the ✕ it sits
   opposite; the header is already a flex row, so it simply takes the leading slot. */
.m-no-m-sheet-back {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-right: 0.25rem;
    padding: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    color: var(--m-fg);
    font-size: 1.125rem;
    cursor: pointer;
    flex-shrink: 0;
}

.m-no-m-sheet-back:active {
    background: var(--m-surface-2);
}

/* A component whose required choice is still missing says so on the affordance itself ("Falta elegir"),
   so the slot does not look finished when it is not. */
.m-no-component-customize.m-no-component-required {
    border-color: var(--m-danger);
    color: var(--m-danger-fg);
}

.m-no-quantity-section,
.m-no-instructions-section {
    margin-bottom: 1rem;
}

/* #532: a modifier from the library, in the dish sheet, for this order only. */
.m-no-library {
    margin-bottom: 1rem;
}

.m-no-library-open,
.m-no-library-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
    min-height: 44px;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--m-border);
    border-radius: 0;
    background: var(--m-surface);
    color: var(--m-fg);
    text-align: left;
}

.m-no-library-open {
    justify-content: center;
    font-weight: 600;
}

.m-no-library-search {
    box-sizing: border-box;
    width: 100%;
    min-height: 44px;
    margin-bottom: 0.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--m-border);
    border-radius: 0;
    background: var(--m-surface);
    color: var(--m-fg);
}

.m-no-library-row + .m-no-library-row {
    border-top: 0;
}

.m-no-library-meta,
.m-no-library-note {
    font-size: 0.875rem;
    opacity: 0.8;
}

.m-no-library-retry {
    border: 0;
    background: none;
    color: inherit;
    font-weight: 600;
    text-decoration: underline;
}

.m-no-quantity-section label,
.m-no-instructions-section label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.5rem;
    color: #374151;
}

.m-no-quantity-controls {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.m-no-qty-btn {
    width: 40px;
    height: 40px;
    border: 2px solid #e5e7eb;
    background: white;
    font-size: 1.25rem;
    cursor: pointer;
}

.m-no-qty-btn:disabled {
    opacity: 0.5;
}

.m-no-qty-value {
    font-size: 1.25rem;
    font-weight: 700;
    min-width: 30px;
    text-align: center;
}

.m-no-instructions-section textarea {
    width: 100%;
    padding: 0.6rem;
    border: 2px solid #e5e7eb;
    resize: none;
    font-family: inherit;
    font-size: 16px;
}

.m-no-cart-body {
    min-height: 150px;
}

.m-no-cart-item {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 0.75rem 0;
    border-bottom: 1px solid #f3f4f6;
}

.m-no-cart-item-info {
    display: flex;
    gap: 0.5rem;
    /* #212 — the NAME yields, not the controls. Without min-width:0 a long flex child refuses
       to shrink below its content and pushes the price, chip and trash out of the row. */
    flex: 1 1 auto;
    min-width: 0;
}

.m-no-cart-item-qty {
    font-weight: 700;
    color: var(--m-coral);
}

.m-no-cart-item-details {
    display: flex;
    flex-direction: column;
    /* #212 — nested flex needs its own min-width:0 or the wrap never reaches the text. */
    min-width: 0;
}

.m-no-cart-item-name {
    font-weight: 500;
}

.m-no-cart-item-mods {
    font-size: 0.75rem;
    color: #6b7280;
}

.m-no-cart-item-notes {
    font-size: 0.75rem;
    color: #f59e0b;
    font-style: italic;
}

.m-no-cart-item-right {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    /* #212 — price, course chip and trash stay on ONE line, always in the same order. Wrapping
       here is what produced two different layouts in the same cart: the controls folded under
       the price when the name was long, and stayed beside it when the name was short. */
    flex-wrap: nowrap;
    flex: 0 0 auto;
    justify-content: flex-end;
}

.m-no-cart-item-price {
    font-weight: 600;
}

.m-no-cart-item-course-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 44px;
    padding: 6px 10px;
    background: transparent;
    color: var(--m-coral);
    border: 1px solid var(--m-coral);
    border-radius: 0;
    font-family: 'Montserrat', sans-serif;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.m-no-cart-item-course-chip:hover:not(:disabled) {
    background: var(--m-coral);
    color: var(--m-chrome);
}

.m-no-btn-remove {
    width: 44px;
    height: 44px;
    /* #212 — never shrink. A 44px tap target that flexes down to a sliver is the screenshot. */
    flex: 0 0 44px;
    background: #fee2e2;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #b91c1c;
    font-size: 0.95rem;
}

.m-no-empty-cart {
    text-align: center;
    padding: 2rem;
    color: #9ca3af;
}

.m-no-cart-footer {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.m-no-cart-totals {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 1rem;
}

.m-no-total-amount {
    font-weight: 700;
    color: var(--m-coral);
    font-size: 1.15rem;
}

.m-no-mobile-toast {
    position: fixed;
    top: calc(0.75rem + env(safe-area-inset-top, 0));
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 240px;
    max-width: calc(100vw - 1.5rem);
    padding: 0.75rem 0.875rem;
    background: #ffffff;
    color: var(--m-fg);
    border-left: 4px solid var(--m-coral);
    border-radius: 0;
    box-shadow: 0 8px 24px rgba(0, 0, 117, 0.18),
                    0 2px 6px rgba(0, 0, 117, 0.08);
    font-family: var(--pt-font-body, 'Montserrat', sans-serif);
    font-weight: 600;
    font-size: 0.875rem;
    z-index: 300;
    animation: m-no-toast-in 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

.m-no-mobile-toast__icon {
    font-size: 1.125rem;
    flex-shrink: 0;
    color: var(--m-coral);
}

.m-no-mobile-toast__message {
    flex: 1;
    line-height: 1.35;
}

.m-no-mobile-toast--success {
    border-left-color: var(--m-coral);
}

.m-no-mobile-toast--success .m-no-mobile-toast__icon {
    color: var(--m-coral);
}

.m-no-mobile-toast--error {
    border-left-color: var(--m-danger);
}

.m-no-mobile-toast--error .m-no-mobile-toast__icon {
    color: var(--m-danger);
}

.m-no-mobile-toast--warning {
    border-left-color: var(--m-warn);
}

.m-no-mobile-toast--warning .m-no-mobile-toast__icon {
    color: var(--m-warn);
}

.m-no-mobile-toast--info {
    border-left-color: var(--m-chrome);
}

.m-no-mobile-toast--info .m-no-mobile-toast__icon {
    color: var(--m-brand);
}

@keyframes m-no-toast-in {
    from {
        opacity: 0;
        transform: translate(-50%, -0.5rem);
    }

    to {
        opacity: 1;
        transform: translate(-50%, 0);
    }
}

@media (prefers-color-scheme: dark) {
    .m-no-mobile-new-order {
        background: #0f172a;
    }

    .m-no-step-header h2 {
        color: #f9fafb;
    }

    .m-no-step-header p {
        color: #9ca3af;
    }

    .m-no-tab-option,
.m-no-menu-item-card {
        background: #1e293b;
        border-color: #334155;
    }

    .m-no-tab-table,
.m-no-item-name {
        color: #f9fafb;
    }

    .m-no-empty-tabs {
        background: #1e293b;
    }

    /* #205 — held names + "Guardar para después" */
    .m-no-held-name {
        background: #1e293b;
        border-color: #334155;
        border-left-color: var(--m-coral);
    }

    .m-no-held-name.m-no-selected {
        background: #0f172a;
        border-color: var(--m-coral);
    }

    .m-no-held-name-text {
        color: #f9fafb;
    }

    .m-no-held-name-discard {
        border-left-color: #334155;
        color: #cbd5e1;
    }

    .m-no-btn-secondary {
        background: transparent;
        color: #f9fafb;
        border-color: #94a3b8;
    }

    .m-no-form-select,
.m-no-form-input {
        background: #1e293b;
        border-color: #334155;
        color: #f9fafb;
    }

    /* #214 */
    .m-no-table-trigger {
        background: #1e293b;
        border-color: #334155;
        color: #f9fafb;
    }

    .m-no-table-option {
        background: #1e293b;
        border-color: #334155;
        border-left-color: var(--m-coral);
        color: #f9fafb;
    }

    .m-no-table-option.m-no-selected {
        background: #0f172a;
        border-color: var(--m-coral);
    }

    .m-no-info-banner {
        background: rgba(251, 191, 36, 0.15);
        color: #fbbf24;
    }

    .m-no-category-tabs {
        background: #1e293b;
        border-bottom-color: #334155;
    }

    .m-no-category-tab {
        background: #1e293b;
        border-color: #334155;
        color: #9ca3af;
    }

    /* #288: the MENU switcher sits directly on top of the category strip and was written with the same
       literal `background: white`, but never got this override — so dark mode left a white band across the
       top of an otherwise dark screen, grey-on-white chips and all. Same surface as the strip below it,
       because the two are read as one stack; the active chip keeps its coral and is unaffected. */
    .m-no-menu-switch {
        background: #1e293b;
    }

    .m-no-menu-chip {
        background: #1e293b;
        border-color: #334155;
        color: #9ca3af;
    }

    .m-no-m-sheet {
        background: #1e293b;
    }

    .m-no-m-sheet-header {
        background: #1e293b;
        border-bottom-color: #334155;
    }

    .m-no-m-sheet-header h2 {
        color: #f9fafb;
    }

    .m-no-m-sheet-close {
        background: #334155;
        color: #f9fafb;
    }

    .m-no-m-sheet-footer {
        border-top-color: #334155;
    }

    .m-no-modifier-group {
        border-bottom-color: #334155;
    }

    .m-no-modifier-name {
        color: #f9fafb;
    }

    .m-no-modifier-option {
        background: #1e293b;
        border-color: #334155;
    }

    /* #208 — #9ca3af is 1.9:1 on the dark row; the unselected ring has to be visible
       before it can be chosen. */
    .m-no-option-check {
        border-color: #94a3b8;
    }

    .m-no-option-name {
        color: #f9fafb;
    }

    .m-no-qty-btn {
        background: #1e293b;
        border-color: #334155;
        color: #f9fafb;
    }

    .m-no-qty-value {
        color: #f9fafb;
    }

    .m-no-instructions-section textarea {
        background: #0f172a;
        border-color: #334155;
        color: #f9fafb;
    }

    .m-no-cart-item {
        border-bottom-color: #334155;
    }

    .m-no-cart-item-name {
        color: #f9fafb;
    }

    .m-no-form-label {
        color: #d1d5db;
    }

    .m-no-quantity-section label,
.m-no-instructions-section label {
        color: #d1d5db;
    }

    .m-no-mobile-toast {
        background: #1e293b;
        color: #f9fafb;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.50),
                        0 2px 6px rgba(0, 0, 0, 0.30);
    }
}

@supports (padding-top: env(safe-area-inset-top)) {
    .m-no-order-header {
        padding-top: calc(0.875rem + env(safe-area-inset-top));
    }

    .m-no-cart-summary {
        padding-bottom: calc(0.875rem + env(safe-area-inset-bottom));
    }

    .m-no-m-sheet-footer {
        padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    }

    /* #214 — the table drawer has no footer, so its LAST ROW is the thing sitting on the home
       indicator. Every other sheet on this page ends in a footer and was covered by the rule
       above; a bottom-pinned sheet whose content runs to the edge was a case this block never
       had. Without it the last table on an iPhone is half under the bar — and no desktop run
       can see it, because @supports env() resolves to zero insets there. */
    .m-no-m-sheet-body:last-child {
        padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    }
}

/* ---- Tab-detail screen (/m/tab/{id}) -------------------------------------------
   Migrated from MobileTabDetail.razor (#54 Phase B). 1275 inline lines, 109 classes —
   the largest of the seven, and the money-path screen, which is why it went last.

   All SEVEN Phase A names lived here; their base rules are gone and only descendants
   and modifiers remain, renamed. This page also held the LAST of the shadowing
   custom-property declarations — --bg-primary, --text-primary, --text-secondary,
   which redefined app.css globals with different values. With this migration the
   guide's rule 3 (no custom property declared in a page <style>) holds across the
   whole Mesero surface.

   --pt-cta resolved to var(--pt-coral) and maps to --m-coral; --pt-chrome resolved to
   var(--pt-navy) and maps to --m-chrome; --pt-neutral (#64748b, secondary text) to
   --m-fg-muted and --pt-neutral-bg to --m-surface-2.

   --pt-destructive-hover needed a new token. It is #dc2626, which is exactly
   --m-danger, while --pt-destructive is the lighter #ef4444 — so mapping both onto
   --m-danger would have made hover identical to base and deleted the state silently.
   --m-danger-hover is added for it. */

.m-td-mobile-tab-detail {
    min-height: 100vh;
    background: var(--m-bg);
    display: flex;
    flex-direction: column;
    padding-bottom: 80px;
    font-variant-numeric: tabular-nums;
}

.m-td-detail-header {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.625rem;
    background: var(--m-chrome);
    color: white;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.m-header-title h1 {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-header-title .m-td-customer-name {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.85);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.m-btn-icon:hover {
    background: rgba(255, 255, 255, 0.2);
}

.m-btn-icon > i {
    font-size: 0.875rem;
}

/* #238 - reads as a LABEL, next to three things that read as buttons.

   It used to paint rgba(255,255,255,0.15) in a square: the same rectangle as .m-btn-icon
   beside it, at a HIGHER alpha than their 0.10, so the one non-control in the row was the
   most prominent thing in it. Outlined-and-round against filled-and-square is the whole
   distinction, which is why the fill is gone rather than dimmed.

   999px does not contradict `--m-radius: 0`. That token is the chrome's straight-edge
   rule; .m-no-menu-chip is already a 999px outlined pill in this same stylesheet.

   Border alpha is 0.45 because the border IS the pill: a lighter hairline at 0.35 composites
   over --m-chrome to 2.70:1, under the 3:1 floor a boundary needs to be seen at all. 0.45
   measures 3.8:1.
   The chrome does not swap between themes ("a navy bar stays a navy bar"), so one
   treatment is correct in both.

   NO ICON, though the mockup has one. Measured at 360px with the kebab showing, the room
   left for the table name is 59px with the chip this replaced, 46px with a glyph in the
   pill, 61px without one -- against the 63-72px the name needs. The header could already
   not fit the name at that width; a decorative glyph taking another 13px of it is not a
   trade worth making, and the shape alone already says "not a button". */
.m-td-tab-position {
    padding: 0.2rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.45);
    border-radius: 999px;
    color: rgba(255, 255, 255, 0.85);
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
}

.m-td-position-count {
    line-height: 1;
}

.m-td-error-icon {
    width: 64px;
    height: 64px;
    background: #fef2f2;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 1rem;
}

.m-td-error-icon i {
    font-size: 2rem;
    color: #dc2626;
}

.m-error-state h2 {
    margin: 0 0 0.5rem;
    color: var(--m-fg);
}

.m-error-state p {
    margin: 0 0 1.5rem;
    color: var(--m-fg-muted);
}

.m-td-btn-retry {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 0;
    background: #3b82f6;
    color: white;
    font-weight: 600;
    cursor: pointer;
}

.m-td-summary-card {
    margin: 0.5rem;
    background: white;
    border-radius: 0;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
    overflow: hidden;
    border-left: 3px solid var(--m-coral);
}

.m-td-summary-row {
    display: flex;
    justify-content: space-around;
    padding: 0.625rem 0.75rem;
}

.m-td-summary-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.m-td-summary-label {
    font-size: 0.6875rem;
    color: var(--m-fg-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-bottom: 0.125rem;
}

.m-td-summary-value {
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--m-fg);
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.m-td-summary-value.m-td-highlight-pending {
    color: var(--m-warn);
}

.m-td-summary-value.m-td-highlight-total {
    color: var(--m-coral);
}

.m-td-paid-banner,
.m-td-cash-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.5rem;
    font-weight: 600;
    font-size: 0.8125rem;
}

.m-td-paid-banner {
    background: #dbeafe;
    color: #1e40af;
}

/* #209 — on the warn tokens, which swap by theme. The literals here were light-mode
   only, and the dark block below overrode just the background: the brown text stayed and
   "Pago en efectivo" sat at about 1.6:1 on its own band. */
.m-td-cash-banner {
    background: var(--m-warn-bg);
    color: var(--m-warn-fg);
}

.m-td-orders-section {
    padding: 0 0.5rem;
    width: 100%;
    box-sizing: border-box;
}

.m-td-section-title {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
    font-weight: 700;
    color: var(--m-fg-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin: 0.75rem 0 0.375rem;
}

.m-td-section-title i {
    color: var(--m-coral);
    font-size: 0.75rem;
}

.m-td-order-card {
    width: 100%;
    max-width: none;
    min-width: 0;
    height: auto;
    max-height: none;
    box-sizing: border-box;
    background: white;
    border-radius: 0;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
    margin-bottom: 0.375rem;
    overflow: hidden;
    border: none;
    border-left: 3px solid transparent;
}

.m-td-order-card:not(.m-td-fulfilled):not(.m-td-cancelled) {
    border-left-color: var(--m-coral);
}

.m-td-order-card.m-td-fulfilled {
    opacity: 0.7;
}

.m-td-order-card.m-td-cancelled {
    opacity: 0.5;
}

.m-td-order-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem 0.75rem;
    background: #f8fafc;
    border-bottom: 1px solid #e5e7eb;
}

.m-td-order-lifecycle-row {
    padding: 0.25rem 0.75rem;
    border-bottom: 1px solid #f1f5f9;
    background: #fdfdfe;
}

.m-td-order-elapsed {
    display: inline-flex;
    align-items: center;
    padding: 1px 6px;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
    border: 1px solid transparent;
    margin-left: 0.125rem;
}

.m-td-order-elapsed.m-td-normal {
    background: rgba(0, 0, 117, 0.06);
    color: #4a4a52;
    border-color: rgba(0, 0, 117, 0.18);
}

.m-td-order-elapsed.m-td-warn {
    background: rgba(245, 158, 11, 0.12);
    color: #b45309;
    border-color: rgba(245, 158, 11, 0.5);
}

.m-td-order-elapsed.m-td-urgent {
    background: var(--m-coral);
    color: var(--m-chrome);
    border-color: var(--m-coral);
}

.m-td-order-elapsed.m-td-escalate {
    background: var(--m-coral);
    color: var(--m-chrome);
    border-color: var(--m-coral);
    box-shadow: 0 0 0 2px rgba(236, 96, 126, 0.25);
}

.m-td-order-card.m-td-escalated {
    border-left-width: 6px;
    border-left-color: var(--m-coral);
    box-shadow: 0 2px 8px rgba(236, 96, 126, 0.22);
}

.m-td-notification-permission-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    margin: 0.5rem;
    background: rgba(236, 96, 126, 0.08);
    border-left: 3px solid var(--m-coral);
    font-family: 'Montserrat', sans-serif;
    font-size: 0.8125rem;
    color: var(--m-fg);
}

.m-td-npb-text {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 1;
    min-width: 0;
}

.m-td-npb-text i {
    color: var(--m-coral);
    flex-shrink: 0;
}

.m-td-npb-actions {
    display: flex;
    gap: 0.375rem;
    flex-shrink: 0;
}

.m-td-npb-btn {
    min-height: 44px;
    padding: 0.5rem 0.875rem;
    background: transparent;
    border: 1px solid var(--m-fg-muted);
    color: var(--m-fg-muted);
    font-family: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
}

.m-td-npb-btn-primary {
    background: var(--m-chrome);
    border-color: var(--m-chrome);
    color: #fff;
}

.m-td-order-status-meta {
    font-weight: 700;
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
    margin-left: 0.125rem;
    opacity: 0.85;
}

.m-td-order-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 1px 6px;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    border: 1px solid transparent;
    margin-left: 0.125rem;
}

.m-td-order-chip.m-td-remake {
    background: var(--m-chrome);
    color: #fff;
    border-color: var(--m-chrome);
}

.m-td-order-chip.m-td-remake i {
    font-size: 0.625rem;
}

.m-td-order-cancel-reason {
    display: flex;
    align-items: flex-start;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    background: rgba(153, 27, 27, 0.06);
    color: #991b1b;
    font-size: 0.75rem;
    line-height: 1.35;
    border-bottom: 1px solid rgba(153, 27, 27, 0.18);
}

.m-td-order-cancel-reason i {
    margin-top: 0.15rem;
    font-size: 0.7rem;
}

.m-td-order-approval-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.25rem;
    border-bottom: 1px dashed rgba(0, 0, 117, 0.08);
}

@media (prefers-color-scheme: dark) {
    .m-td-order-approval-row {
        border-bottom-color: rgba(255, 255, 255, 0.10);
    }
}

.m-td-summary-kpi {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    margin-top: 0.5rem;
    background: rgba(0, 0, 117, 0.04);
    font-size: 0.75rem;
    color: var(--m-fg-muted);
    border-left: 3px solid var(--m-chrome);
}

.m-td-summary-kpi i {
    color: var(--m-brand);
    font-size: 0.75rem;
}

.m-td-summary-kpi strong {
    color: var(--m-fg);
    font-variant-numeric: tabular-nums;
}

.m-td-order-info {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    flex-wrap: wrap;
}

.m-td-order-time {
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--m-fg);
    font-variant-numeric: tabular-nums;
}

.m-td-order-status {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0;
    font-size: 1.0625rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    text-transform: uppercase;
}

.m-td-order-status::before {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.m-td-order-status.m-td-pending {
    color: #b45309;
}

.m-td-order-status.m-td-queued {
    color: #6f42c1;
}

.m-td-order-status.m-td-ready {
    color: #0c5460;
}

.m-td-order-status.m-td-fulfilled {
    color: #065f46;
}

.m-td-order-status.m-td-cancelled {
    color: #991b1b;
}

.m-td-order-status > i {
    display: none;
}

.m-td-order-total {
    font-weight: 700;
    font-size: 0.8125rem;
    color: var(--m-coral);
    font-variant-numeric: tabular-nums;
}

.m-td-order-items {
    padding: 0.375rem 0.625rem;
}

.m-td-order-item {
    padding: 0.25rem 0;
    border-bottom: 1px solid #f3f4f6;
}

.m-td-order-item:last-child {
    border-bottom: none;
}

.m-td-order-item.m-td-comp-item {
    opacity: 0.6;
    text-decoration: line-through;
}

.m-td-item-main {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    font-size: 0.8125rem;
}

.m-td-item-qty {
    font-weight: 700;
    color: var(--m-coral);
    min-width: 22px;
    font-variant-numeric: tabular-nums;
}

.m-td-item-name {
    flex: 1;
    color: var(--m-fg);
}

.m-td-item-price {
    color: var(--m-fg-muted);
    font-weight: 500;
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
}

.m-td-item-modifiers {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 0.375rem;
    margin-top: 0.15rem;
    padding-left: 1.625rem;
}

.m-td-modifier {
    font-size: 0.6875rem;
    color: var(--m-fg-muted);
}

.m-td-modifier + .m-td-modifier::before {
    content: "·";
    margin-right: 0.25rem;
    color: #d1d5db;
}

.m-td-item-notes {
    display: flex;
    align-items: flex-start;
    gap: 0.25rem;
    margin-top: 0.15rem;
    padding-left: 1.625rem;
    font-size: 0.6875rem;
    color: #f59e0b;
    font-style: italic;
}

.m-td-item-notes i {
    margin-top: 0.1rem;
    font-size: 0.625rem;
}

.m-td-order-actions {
    padding: 0.375rem 0.625rem;
    border-top: 1px solid #f3f4f6;
    display: flex;
    /* #211 — explicit: every control in the row is one height. The kebab used to opt out by
       pinning its own, so a wrapped label grew the buttons and left it short. */
    align-items: stretch;
    gap: 0.5rem;
}

.m-td-btn-fulfill,
.m-td-btn-cancel,
.m-td-btn-release-now {
    flex: 1;
    display: flex;
    align-items: center;
    /* #211 follow-up — `center` is what Cam saw as inconsistent icon positioning: centring
       the icon+label group makes each icon's distance from its border depend on its OWN
       label length, so a 13-character label and a 16-character one can never agree. Left
       aligned, both glyphs sit exactly one padding in, by construction. */
    justify-content: flex-start;
    gap: 0.5rem;
    min-height: 44px;
    padding: 0.5rem;
    border-radius: 0;
    /* #211 — ONE declaration. This rule carried font-size twice (0.8125rem then 0.9rem) and
       the later one won, which is what pushed "Marcar entregado" onto a second line inside a
       flex:1 button sharing the row with "Liberar ahora". */
    font-size: 0.8125rem;
    font-weight: 600;
    min-width: 0;
    cursor: pointer;
    transition: all 0.2s;
}

/* #211 follow-up — the label stacks onto two rows instead of being held on one.
   `white-space: nowrap` gave "Marcar entregado" a single line inside a ~115px budget that
   the label needs ~115px to fill, and with no `overflow` rule the excess spilled straight
   through the padding and across the border — the overflow in the card's second screenshot.
   Stacking costs ~2px of button height (44 -> 46) and removes the overflow outright, rather
   than trading it for a smaller font on every phone. The words are separate elements, not a
   wrap opportunity, so BOTH buttons stack identically regardless of how much room the row
   happens to give them — in a row they share. A button alone in its row keeps one line (#263,
   below). */
.m-td-btn-label {
    display: flex;
    flex-direction: column;
    /* Shrink-to-fit on the cross axis. The default `stretch` would widen each word's box to
       the full label column, which makes the rendered text width unmeasurable — a fit test
       against a stretched box compares the budget with itself and can never fail. */
    align-items: flex-start;
    flex: 1 1 auto;
    min-width: 0;
    line-height: 1.15;
    text-align: left;
}

/* #263 — the stack is for a row that SHARES its width. With "Liberar ahora" beside it each button gets
   about half the row, and "Marcar entregado" needed the two rows above; for an order the kitchen already
   has there is no "Liberar ahora", the button has the row to itself, and stacking left a two-line label in
   all that room. So without that button in the row the words run on one line, a space apart.
   Written as :not(:has()) on purpose: a WebView without :has() drops the whole rule and keeps #211's
   stack, which fits everywhere, rather than a single line that overflows the shared row. */
.m-td-order-actions:not(:has(> .m-td-btn-release-now)) .m-td-btn-label {
    flex-direction: row;
    align-items: baseline;
    column-gap: 0.28em;
}

/* #211 — one icon column. fa-bolt and fa-check have different intrinsic widths, so a shared
   `gap` alone still left visibly different space before each label. 0.875rem matches the
   kebab's glyph so all three controls read as one set. */
.m-td-btn-fulfill > i,
.m-td-btn-cancel > i,
.m-td-btn-release-now > i {
    flex: 0 0 auto;
    width: 1rem;
    text-align: center;
    font-size: 0.875rem;
}

.m-td-btn-fulfill {
    background: white;
    color: var(--m-coral);
    border: 1px solid var(--m-coral);
}

.m-td-btn-fulfill:hover:not(:disabled) {
    background: rgba(236, 96, 126, 0.08);
}

.m-td-btn-cancel {
    background: white;
    color: var(--m-danger);
    border: 1px solid var(--m-danger);
}

.m-td-btn-cancel:hover:not(:disabled) {
    background: var(--m-danger-bg);
}

.m-td-btn-release-now {
    background: white;
    color: #6f42c1;
    border: 1px solid #6f42c1;
}

.m-td-btn-release-now:hover:not(:disabled) {
    background: rgba(111, 66, 193, 0.08);
}

.m-td-btn-fulfill:disabled,
.m-td-btn-cancel:disabled,
.m-td-btn-release-now:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.m-td-btn-fulfill.m-td-loading,
.m-td-btn-cancel.m-td-loading,
.m-td-btn-release-now.m-td-loading {
    background: #f3f4f6;
    color: #6b7280;
    border-color: #d1d5db;
}

.m-td-orow-cancel-progress {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 44px;
    padding: 0 12px;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--m-fg-muted);
    background: var(--m-surface-2);
    border: 1px solid rgba(0, 0, 117, 0.10);
    flex-shrink: 0;
}

.m-td-btn-repeat-order {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 44px;
    padding: 0.5rem;
    border: none;
    border-radius: 0;
    background: var(--m-chrome);
    color: white;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s;
}

.m-td-btn-repeat-order:hover:not(:disabled) {
    background: #00005c;
}

.m-td-btn-repeat-order:disabled {
    opacity: 0.7;
    cursor: not-allowed;
}

.m-td-btn-repeat-order.m-td-loading {
    background: #6b7280;
}

.m-td-btn-repeat-last {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    min-height: 44px;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--m-chrome);
    border-radius: 0;
    background: white;
    color: var(--m-brand);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}

.m-td-btn-repeat-last:hover:not(:disabled) {
    background: var(--m-chrome);
    color: white;
}

.m-td-btn-repeat-last:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.m-td-btn-mark-bill-requested {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    margin-left: 0.5rem;
    min-height: 44px;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--m-coral);
    border-radius: 0;
    background: white;
    color: var(--m-coral);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}

.m-td-btn-mark-bill-requested:hover:not(:disabled) {
    background: var(--m-coral);
    color: var(--m-chrome);
}

.m-td-btn-mark-bill-requested:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.m-td-bill-requested-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.5rem;
    padding: 0.375rem 0.625rem;
    border-radius: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    background: rgba(0, 0, 117, 0.08);
    color: var(--m-brand);
    border: 1px solid rgba(0, 0, 117, 0.25);
}

.m-td-bill-requested-chip.m-td-urgent {
    background: var(--m-warn-bg);
    color: #92400e;
    border-color: var(--m-warn);
}

.m-td-empty-orders {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 3rem 1rem;
    color: var(--m-fg-muted);
}

.m-td-empty-orders i {
    font-size: 2.5rem;
    color: #d1d5db;
    margin-bottom: 1rem;
}

.m-td-bottom-actions {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    background: white;
    border-top: 1px solid #e5e7eb;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.08);
}

/* #213 — the footer's two actions share the width instead of shrink-wrapping their labels.
   `.m-btn-action` declares no horizontal padding at all, so the glyph sat flush against the
   button edge; the icon column matches the one the per-order row uses (#211) so every button
   on this screen spaces its icon the same way. */
.m-td-bottom-actions > .m-btn-action {
    flex: 1 1 0;
    min-width: 0;
    /* #213 follow-up — the labels were left at the inherited 16px (`.m-btn-action` declares no
       font-size of its own), so once both buttons shared the row "Entregar todo (1)" no longer
       fitted: it wrapped, the primary grew taller than the secondary beside it, and the footer
       took MORE of the screen than before the fix. 0.8125rem is the size the per-order buttons
       directly above already use, so the two rows now read as one set.

       `nowrap` guarantees the label cannot stack — but on its own it only converts a wrap into
       an OVERFLOW, which is precisely what it did to "Marcar entregado" on #211. So it is paired
       with a contract test measuring the longest realistic label against the button's real
       budget; the card warns the count is often two digits, hence "Entregar todo (12)". */
    font-size: 0.8125rem;
    white-space: nowrap;
    padding: 0.5rem 0.875rem;
    box-sizing: border-box;
    /* Both buttons reserve the SAME 1px, so the row splits evenly. box-sizing alone does not do
       it: a flex item's base size cannot shrink below its own border, so with `flex-basis: 0`
       the outlined secondary starts at 2px and the borderless primary at 0, and they grow by
       equal amounts from there — 180px against 178px, which is what the contract test caught.
       The secondary's own rule below re-colours this border; the primary keeps it invisible. */
    border: 1px solid transparent;
}

.m-td-bottom-actions > .m-btn-action > i {
    flex: 0 0 auto;
    width: 1rem;
    text-align: center;
    font-size: 0.875rem;
}

/* #213 (Cam, 13 Sep) — each LABEL on its button's centre, the icon beside it. Centring icon and label as one
   group left every label right of centre by half the icon and its gap: under a thin "+" that read as
   "Agregar pedido" sitting off-centre, while the heavier double check hid the same offset on "Entregar todo".
   This empty item mirrors the icon and its gap on the label's other side, so the label lands on the centre.
   Its negative margin cancels the gap the row adds before it, and it may shrink to nothing: when a long count
   leaves no room to spare, the balance gives way and the label keeps every pixel it had. */
.m-td-bottom-actions > .m-btn-action::after {
    content: "";
    flex: 0 1 1.5rem;
    margin-left: -0.5rem;
}

.m-btn-action.m-td-primary {
    background: var(--m-chrome);
    color: white;
}

.m-btn-action.m-td-primary:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(0, 0, 117, 0.4);
}

.m-btn-action.m-td-secondary {
    background: white;
    color: var(--m-chrome);
    border: 1px solid var(--m-chrome);
}

.m-btn-action.m-td-secondary:hover:not(:disabled) {
    background: rgba(0, 0, 117, 0.05);
}

.m-btn-action:disabled {
    opacity: 0.7;
    cursor: not-allowed;
}

.m-btn-action.m-td-loading {
    background: #6b7280;
}

/* Mesero close parity MC3 (#429) — "Cobrar" gets a full-width row of its own above the #213 pair.
   Wrapping, not a third column: three labels abreast is exactly the overflow #213 was about, and
   the pair's own rules (equal split, 1px reserved edge, the ::after balance) keep working on the
   second line untouched. The button inherits the pair's skin — m-td-primary once nothing is left
   to deliver, m-td-secondary while "Entregar todo" is the job. */
.m-td-bottom-actions.m-td-with-close {
    flex-wrap: wrap;
}

.m-td-bottom-actions > .m-btn-action.m-td-close-btn {
    flex: 1 0 100%;
}

/* The footer grows by one 44px row and the 0.5rem gap: 65px -> 117px. The page keeps the same
   15px of air above it that the one-row footer had with 80px. */
.m-td-mobile-tab-detail.m-td-with-close {
    padding-bottom: 132px;
}

@media (prefers-color-scheme: dark) {
    .m-td-summary-card,
.m-td-order-card {
        background: #1e293b;
    }

    .m-td-order-header {
        background: #0f172a;
        border-bottom-color: #334155;
    }

    .m-td-order-item {
        border-bottom-color: #334155;
    }

    .m-td-order-actions {
        border-top-color: #334155;
    }

    .m-td-order-status.m-td-pending {
        color: #fbbf24;
    }

    .m-td-order-status.m-td-queued {
        color: #c4b5fd;
    }

    .m-td-order-status.m-td-ready {
        color: #7dd3fc;
    }

    .m-td-order-status.m-td-fulfilled {
        color: #34d399;
    }

    .m-td-order-status.m-td-cancelled {
        color: #f87171;
    }

    .m-td-order-lifecycle-row {
        background: #0b1426;
        border-bottom-color: #334155;
    }

    .m-td-order-elapsed.m-td-normal {
        background: rgba(255, 255, 255, 0.06);
        color: #c0c2c8;
        border-color: rgba(255, 255, 255, 0.16);
    }

    .m-td-order-elapsed.m-td-warn {
        background: rgba(245, 158, 11, 0.18);
        color: #fcd34d;
        border-color: rgba(245, 158, 11, 0.45);
    }

    .m-td-bill-requested-chip {
        background: rgba(255, 255, 255, 0.08);
        color: #c0c2c8;
        border-color: rgba(255, 255, 255, 0.18);
    }

    .m-td-bill-requested-chip.m-td-urgent {
        background: rgba(245, 158, 11, 0.18);
        color: #fbbf24;
        border-color: rgba(245, 158, 11, 0.45);
    }

    .m-td-btn-mark-bill-requested {
        background: #1e293b;
    }

    .m-td-order-card.m-td-escalated {
        box-shadow: 0 2px 8px rgba(236, 96, 126, 0.35);
    }

    .m-td-notification-permission-banner {
        background: rgba(236, 96, 126, 0.15);
        color: #f9fafb;
    }

    .m-td-npb-btn {
        border-color: #9ca3af;
        color: #c0c2c8;
    }

    .m-td-npb-btn-primary {
        color: #fff;
    }

    .m-td-order-cancel-reason {
        background: rgba(248, 113, 113, 0.10);
        color: #fca5a5;
        border-bottom-color: rgba(248, 113, 113, 0.22);
    }

    .m-td-summary-kpi {
        background: rgba(170, 178, 255, 0.08);
        color: #c0c2c8;
        border-left-color: #aab2ff;
    }

    .m-td-summary-kpi i {
        color: #aab2ff;
    }

    .m-td-summary-kpi strong {
        color: #f9fafb;
    }

    .m-td-modifier {
        color: #cbd5e1;
    }

    .m-td-modifier + .m-td-modifier::before {
        color: #475569;
    }

    .m-td-bottom-actions {
        background: #1e293b;
        border-top-color: #334155;
    }

    .m-btn-action.m-td-secondary {
        background: transparent;
        color: #c0c8ff;
        border-color: #c0c8ff;
    }

    .m-btn-action.m-td-secondary:hover:not(:disabled) {
        background: rgba(192, 200, 255, 0.10);
    }

    .m-td-btn-fulfill {
        background: transparent;
        color: var(--m-coral);
        border-color: var(--m-coral);
    }

    .m-td-btn-fulfill:hover:not(:disabled) {
        background: rgba(236, 96, 126, 0.14);
    }

    .m-td-btn-cancel {
        background: transparent;
        color: #f87171;
        border-color: #f87171;
    }

    .m-td-btn-cancel:hover:not(:disabled) {
        background: rgba(248, 113, 113, 0.14);
    }

    .m-td-btn-release-now {
        background: transparent;
        color: #a78bfa;
        border-color: #a78bfa;
    }

    .m-td-btn-release-now:hover:not(:disabled) {
        background: rgba(167, 139, 250, 0.14);
    }

    .m-td-btn-fulfill.m-td-loading,
.m-td-btn-cancel.m-td-loading,
.m-td-btn-release-now.m-td-loading {
        background: rgba(255, 255, 255, 0.06);
        color: #94a3b8;
        border-color: rgba(255, 255, 255, 0.18);
    }

    .m-td-error-icon {
        background: rgba(220, 38, 38, 0.2);
    }

    /* #209 — same defect as the cash banner beside it: the tint went dark and the
       navy-blue foreground did not follow. The cash banner needs no dark rule at all
       now — its tokens swap themselves. */
    .m-td-paid-banner {
        background: rgba(59, 130, 246, 0.2);
        color: #93c5fd;
    }
}

.m-td-pending-claims-section {
    padding: 0 0.5rem;
    margin-bottom: 0.5rem;
}

/* MC4: brand, not coral. Coral text on the light page is 3.07:1 at this 13px bold — under AA; the brand
   token is navy in light (16:1) and coral in dark, where coral on the dark page reads 5.74:1. */
.m-td-pending-claims-title {
    color: var(--m-brand) !important;
}

.m-td-pending-claims-title i {
    color: var(--m-coral);
}

.m-td-pending-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 0.375rem;
    margin-left: 0.375rem;
    background: var(--m-coral);
    color: var(--m-chrome);
    border-radius: 0;
    font-size: 0.6875rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.m-td-claim-card {
    background: var(--m-surface);
    border-radius: 0;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
    margin-bottom: 0.375rem;
    overflow: hidden;
    border-left: 3px solid var(--m-coral);
    padding: 0.5rem 0.625rem;
}

.m-td-claim-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.25rem;
}

.m-td-claim-payer {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--m-fg);
}

.m-td-claim-payer i {
    color: var(--m-coral);
    font-size: 0.75rem;
}

.m-td-claim-time {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.6875rem;
    color: var(--m-fg-muted);
    font-variant-numeric: tabular-nums;
}

.m-td-claim-tender {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    margin: 0.25rem 0 0.5rem;
    padding: 0.2rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 700;
    color: var(--m-on-chrome);
    width: fit-content;
}

.m-td-claim-tender.m-td-claim-tender-cash {
    background: var(--m-chrome);
}

/* MC4: the label was white on coral, 3.21:1 at 11px bold — under the 4.5:1 AA floor. */
.m-td-claim-tender.m-td-claim-tender-terminal {
    background: var(--m-coral);
    color: var(--m-on-coral);
}

.m-td-claim-amount-row {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

/* MC4: 18px bold is not "large" text (that starts at 18.66px bold), so it needs 4.5:1 — coral on the white card
   was 3.21:1. The brand is navy on white and coral on the dark card, 4.65:1. */
.m-td-claim-amount {
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--m-brand);
    font-variant-numeric: tabular-nums;
}

.m-td-claim-tip {
    font-size: 0.6875rem;
    color: var(--m-fg-muted);
    font-variant-numeric: tabular-nums;
}

.m-td-claim-actions {
    display: flex;
    gap: 0.375rem;
}

.m-td-btn-claim-cancel,
.m-td-btn-claim-confirm {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    padding: 0.5rem 0.75rem;
    min-height: var(--m-touch-min);
    border: none;
    border-radius: 0;
    font-weight: 600;
    font-size: 0.8125rem;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.m-td-btn-claim-cancel {
    background: #f3f4f6;
    color: var(--m-fg);
    border: 2px solid #e5e7eb;
}

.m-td-btn-claim-cancel:hover:not(:disabled) {
    background: #fee2e2;
    border-color: #dc2626;
    color: #dc2626;
}

/* MC4: the card's primary is a primary CTA, so it takes the brand pair like every other (guide §1.3). The
   emerald it replaces carried white at 2.54:1. No hover state: this is a phone, and a sticky hover on
   --m-brand-hover would carry the dark label at 4.36:1. */
.m-td-btn-claim-confirm {
    background: var(--m-brand);
    color: var(--m-on-brand);
}

.m-td-btn-claim-cancel:disabled,
.m-td-btn-claim-confirm:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* MC8 (D8): in place of "Confirmar" on a colleague's tab. Text rather than a disabled button: nothing here will
   become pressable, and a greyed button reads as "wait". --m-fg-muted on --m-surface is 4.98:1 light, 5.76:1 dark. */
.m-td-claim-not-mine {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--m-touch-min);
    padding: 0.5rem 0.75rem;
    font-size: 0.8125rem;
    color: var(--m-fg-muted);
    text-align: center;
}

@supports (padding-top: env(safe-area-inset-top)) {
    .m-td-detail-header {
        padding-top: calc(0.5rem + env(safe-area-inset-top));
    }

    .m-td-bottom-actions {
        padding-bottom: calc(0.625rem + env(safe-area-inset-bottom));
    }

    .m-td-mobile-tab-detail {
        padding-bottom: calc(80px + env(safe-area-inset-bottom));
    }

    .m-td-mobile-tab-detail.m-td-with-close {
        padding-bottom: calc(132px + env(safe-area-inset-bottom));
    }
}

@media (prefers-color-scheme: dark) {
    .m-td-btn-claim-cancel {
        background: #334155;
        border-color: #475569;
        color: #f9fafb;
    }

    .m-td-btn-claim-cancel:hover:not(:disabled) {
        background: rgba(220, 38, 38, 0.2);
        border-color: #ef4444;
        color: #f87171;
    }
}

.m-td-handoff-banner {
    background: #ffffff;
    color: var(--m-brand);
    border-left: 4px solid var(--m-coral);
    padding: 0.75rem;
    margin: 0.75rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

.m-td-handoff-banner-head {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    font-size: 0.8125rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-bottom: 0.375rem;
}

.m-td-handoff-banner-head i {
    color: var(--m-coral);
}

.m-td-handoff-banner-body {
    font-size: 0.9375rem;
    font-style: italic;
    margin-bottom: 0.5rem;
    white-space: pre-wrap;
    word-break: break-word;
}

.m-td-handoff-banner-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.m-td-handoff-banner-attr {
    font-size: 0.75rem;
    color: #555;
}

.m-td-handoff-banner-dismiss {
    min-height: 44px;
    min-width: 44px;
    padding: 0.375rem 0.875rem;
    background: var(--m-chrome);
    color: #fff;
    border: none;
    border-radius: 0;
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    font-size: 0.8125rem;
    cursor: pointer;
}

.m-td-handoff-banner-dismiss:hover {
    opacity: 0.92;
}

@media (prefers-color-scheme: dark) {
    .m-td-handoff-banner {
        background: #1a1a2e;
        color: #f0f0f5;
        border-left-color: var(--m-coral);
    }

    .m-td-handoff-banner-attr {
        color: #aaa;
    }
}

.m-td-course-row-chip {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.5rem 0;
}

.m-td-course-chip {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    background: rgba(236, 96, 126, 0.12);
    color: var(--m-coral);
    border: 1px solid var(--m-coral);
    border-radius: 0;
    font-size: 0.75rem;
    font-weight: 600;
}

.m-td-course-status-pill {
    font-size: 0.75rem;
    padding: 4px 10px;
    border-radius: 0;
    font-weight: 500;
    background: #fef3c7;
    color: #92400e;
}

.m-td-course-status-pill[data-test-released="true"] {
    background: rgba(0, 0, 117, 0.08);
    color: var(--m-brand);
}

.m-td-btn-fire-course {
    min-height: 44px;
    padding: 6px 14px;
    background: var(--m-chrome);
    color: #ffffff;
    border: 1px solid var(--m-chrome);
    border-radius: 0;
    font-family: 'Montserrat', sans-serif;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.m-td-btn-fire-course:hover:not(:disabled) {
    filter: brightness(0.95);
}

.m-td-btn-override-course {
    min-height: 44px;
    padding: 6px 12px;
    background: transparent;
    color: var(--m-danger);
    border: 1px solid var(--m-danger);
    border-radius: 0;
    font-family: 'Montserrat', sans-serif;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.m-td-btn-override-course:hover:not(:disabled) {
    background: var(--m-danger-hover);
    color: #ffffff;
}

.m-td-kebab-overlay {
    position: fixed;
    inset: 0;
    background: transparent;
    z-index: 200;
}

.m-td-kebab-menu {
    position: absolute;
    top: 56px;
    right: 8px;
    min-width: 220px;
    background: #ffffff;
    border: 1px solid rgba(0, 0, 117, 0.12);
    border-top: 4px solid var(--m-chrome);
    border-radius: 0;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.15);
    z-index: 210;
    display: flex;
    flex-direction: column;
}

.m-td-kebab-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 44px;
    padding: 10px 14px;
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(0, 0, 117, 0.06);
    color: var(--m-brand);
    font-family: 'Montserrat', sans-serif;
    font-size: 0.9rem;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
}

.m-td-kebab-item:last-child {
    border-bottom: none;
}

.m-td-kebab-item:hover:not(:disabled) {
    background: rgba(0, 0, 117, 0.05);
}

.m-td-kebab-item:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

@media (prefers-color-scheme: dark) {
    .m-td-kebab-menu {
        background: #1f2937;
        border-color: rgba(255, 255, 255, 0.12);
    }

    .m-td-kebab-item {
        color: #f9fafb;
        border-bottom-color: rgba(255, 255, 255, 0.08);
    }

    .m-td-kebab-item:hover:not(:disabled) {
        background: rgba(255, 255, 255, 0.05);
    }

    .m-td-course-status-pill {
        background: rgba(245, 158, 11, 0.18);
        color: #fbbf24;
    }

    .m-td-course-status-pill[data-test-released="true"] {
        background: rgba(255, 255, 255, 0.10);
        color: #ffffff;
    }
}

/* =====================================================================================
   #221 — the course picker (Components/Util/CourseLinePicker.razor)

   These rules used to live in an inline <style> block inside the component. That is why
   the drawer still carried the pre-#208 radio, the pre-#215 narrow footer buttons and a
   backdrop nobody revisited: the CSS-contract harness parses LINKED stylesheets, so an
   inline <style> is invisible to it and no test could ever have covered this surface.
   Moving the rules here puts the drawer under the same CI that guards the rest of Mesero.

   The component renders only from MobileNewOrder (the cart sheet), so mesero.css is the
   correct home — it is not an admin surface.
   ===================================================================================== */

.clp-overlay {
    position: fixed;
    inset: 0;
    /* Was rgba(0, 0, 117, 0.55) — navy. The colour matches every other Mesero scrim.

       NO backdrop-filter, although .m-no-m-sheet-overlay has one — and that difference is the
       second half of #221. This picker opens ON TOP OF the cart sheet, so a blur here smears
       the cart into a translucent pane; in dark mode the cart is slate-blue (#1e293b) and the
       header above it navy, so the pane reads as blue. The first #221 fix swapped navy for
       black but kept the blur, and Cam reported the same "blue transparent overlay" again.
       A sheet stacked on a sheet dims without blurring, which is also what the guest app's
       stacked modal does (.ion-modal-stacked). The blur on the new-order sheets was never an
       authored choice either — it was inherited from app.css's .modal-overlay (see
       mesero-ui-guide.md). */
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    /* Deliberately above .m-no-m-sheet-overlay (200): this picker opens FROM the cart
       sheet and has to paint over it. */
    z-index: 1150;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.2s ease, visibility 0.2s ease;
}

.clp-overlay.visible {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

.clp-sheet {
    background: white;
    width: 100%;
    max-width: 520px;
    max-height: 80vh;
    overflow-y: auto;
    border-radius: 0;
    border-top: 4px solid var(--m-chrome, #000075);
    transform: translateY(24px);
    transition: transform 0.2s ease;
}

.clp-sheet.active {
    transform: translateY(0);
}

.clp-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 18px;
    background: var(--m-chrome, #000075);
    color: #ffffff;
}

.clp-header h5 {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
}

.clp-close {
    min-width: 44px;
    min-height: 44px;
    background: transparent;
    border: 0;
    color: #ffffff;
    font-size: 18px;
    cursor: pointer;
}

.clp-body {
    padding: 12px 18px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.clp-option {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: var(--m-touch-min, 44px);
    padding: 10px 12px;
    border: 1px solid #e5e7eb;
    border-radius: 0;
    cursor: pointer;
}

/* #221 — the same drawn radio as the modifier list (#208), on the native input rather
   than a span beside it. `accent-color` on a stock radio renders differently on iOS
   WebKit and Android Chrome and sizes its dot by platform convention, which is what Cam
   read as "too small / doesn't match". `appearance: none` hands us the box while the
   input keeps real radio semantics, keyboard control and the name-group behaviour. */
.clp-option input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    margin: 0;
    box-sizing: border-box;
    /* 4.78:1 on the white row — the same ring colour and contrast rationale as
       .m-no-option-check; #9ca3af would be 2.54:1, under the 3:1 floor for a control
       boundary (WCAG 1.4.11). */
    border: 2px solid #6b7280;
    border-radius: 50%;
    background: transparent;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

/* The fill is a radial-gradient, NOT an ::after. A pseudo-element on an <input> is not
   generated by every engine — it is the reason Bootstrap draws its own radios with a
   background image rather than a pseudo — and this control has to render identically in
   the Android WebView and iOS WKWebView. 5px radius = the same 10px dot as #208. */
.clp-option input[type="radio"]:checked {
    border-color: var(--m-coral, #ec607e);
    background-image: radial-gradient(circle at center,
                                      var(--m-coral, #ec607e) 0 5px,
                                      transparent 5px);
}

/* Focus ring. `appearance: none` takes the platform control's own focus affordance away
   with it, and drawing our own radio left nothing in its place — a keyboard user had no
   way to see which option was selected. The app's newer interactive elements state this
   as :focus-visible with a coral outline (app.css — .mi-tb-btn, .mi-row, .lib-rail-manage),
   so follow that rather than invent a treatment. :focus-visible, not :focus, so the ring
   answers the keyboard and stays out of the way of a tap. outline-offset holds it clear of
   the 20px circle, whose own border is already coral once checked. */
.clp-option input[type="radio"]:focus-visible {
    outline: 2px solid var(--m-coral, #ec607e);
    outline-offset: 2px;
}

.clp-option-label {
    font-size: 14px;
    font-weight: 500;
    color: #1f2937;
    flex: 1;
    min-width: 0;
}

.clp-default-tag {
    font-size: 11px;
    color: var(--m-chrome, #000075);
    text-transform: lowercase;
    font-style: italic;
}

.clp-footer {
    display: flex;
    gap: 8px;
    padding: 12px 18px;
    /* The sheet is bottom-pinned, so the footer is the last thing above the iOS home
       indicator — same safe-area treatment as the table drawer (#214). */
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
    border-top: 1px solid #e5e7eb;
    background: white;
    position: sticky;
    bottom: 0;
}

/* #221 — full width, split evenly. These were `min-width: 96px` against
   `justify-content: flex-end`, so they huddled in the right corner while every other
   primary action in Mesero spans its container (#215). `flex: 1 1 0` with border-box
   makes the pair fill the row and measure equal. */
.clp-btn {
    flex: 1 1 0;
    min-width: 0;
    min-height: var(--m-touch-min, 44px);
    box-sizing: border-box;
    padding: 10px 16px;
    border-radius: 0;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}

.clp-btn-cancel {
    background: transparent;
    color: var(--m-chrome, #000075);
    border: 1px solid var(--m-chrome, #000075);
}

.clp-btn-apply {
    background: var(--m-coral, #ec607e);
    /* The fallback is NAVY, deliberately. --pt-on-coral resolves to var(--pt-navy) in
       app.css, which measures 5.20:1 on coral; white would be 3.21:1 and fail 1.4.3. A
       white fallback here looks like the safe default and is the inaccessible one. */
    color: var(--pt-on-coral, #000075);
    border: 1px solid var(--m-coral, #ec607e);
}

.clp-btn-apply:hover:not(:disabled) {
    filter: brightness(0.95);
}

@media (prefers-color-scheme: dark) {
    .clp-sheet {
        background: #1f2937;
        color: #f9fafb;
    }

    .clp-option {
        border-color: #374151;
    }

    .clp-option-label {
        color: #f9fafb;
    }

    /* Unselected ring on the dark row — same value the modifier radio uses. */
    .clp-option input[type="radio"] {
        border-color: #94a3b8;
    }

    .clp-default-tag {
        color: #ffffff;
    }

    .clp-footer {
        background: #1f2937;
        border-top-color: #374151;
    }

    .clp-btn-cancel {
        color: #ffffff;
        border-color: #ffffff;
    }
}

/* =============================================================================
   MSheet — the canonical Mesero bottom sheet (Components/Mobile/MSheet.razor)
   =============================================================================
   mesero-ui-guide.md §3.1, built once, the first time a new sheet was needed
   (Mesero close parity MC3). The four existing sheet dialects are not migrated by
   this block; it is where they migrate TO.

   Every line below is the spec, not a choice made here:
   - backdrop black at 0.5, never navy, no backdrop-filter (#221 — a sheet over a
     sheet dims, it does not blur);
   - flush to the viewport edges: the overlay has no padding;
   - at most 85vh, and the BODY is the only scroll container — head and foot stay put;
   - 3px coral top accent;
   - the safe-area inset padded ONCE, on the sheet (MobileNewOrder pads twice);
   - enter translateY(100%) -> 0 over .25s on the guide's curve (the shared
     m-slide-up keyframe is exactly that transform).
   ============================================================================= */
.m-sheet-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--m-z-sheet);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 0;
    background: rgba(0, 0, 0, 0.5);
}

.m-sheet {
    width: 100%;
    max-height: 85vh;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    background: var(--m-surface);
    color: var(--m-fg);
    border-top: 3px solid var(--m-coral);
    border-radius: var(--m-radius);
    padding-bottom: env(safe-area-inset-bottom);
    animation: m-slide-up 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.m-sheet-head {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.25rem 0.25rem 0.25rem 1rem;
    border-bottom: 1px solid var(--m-border);
}

.m-sheet-head h2 {
    margin: 0;
    min-width: 0;
    font-family: var(--pt-font-logo);
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.25;
    color: var(--m-fg);
    overflow-wrap: anywhere;
}

/* 44x44 hit box. Muted glyph: the close is always there and never the point. */
.m-sheet-close {
    flex: 0 0 auto;
    width: var(--m-touch-min);
    height: var(--m-touch-min);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: var(--m-radius);
    background: transparent;
    color: var(--m-fg-muted);
    font-size: 1.25rem;
    cursor: pointer;
}

.m-sheet-close:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.m-sheet-close:focus-visible {
    outline: 2px solid var(--m-coral);
    outline-offset: -2px;
}

.m-sheet-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 1rem;
}

.m-sheet-foot {
    flex-shrink: 0;
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--m-border);
}

/* =============================================================================
   "Cobrar" — the close sheet (Components/Mobile/MobileCloseSheet.razor, MC3 #429)
   =============================================================================
   Colour follows the tokens only, so dark mode needs no block of its own.
   - Selection (tender, tip, "Propina va con") is the Mesero tile idiom — the
     table picker's: a 2px edge that turns coral, the fill moving to --m-surface-2.
     Coral clears the 3:1 a state indicator needs on both surfaces (3.2:1 on white,
     4.56:1 on the dark surface); the label stays --m-fg, so no text sits in coral.
   - Alerts carry their tone on the rail and the icon, the sentence stays --m-fg:
     --m-danger-fg on --m-danger-bg is 3.95:1, under the 4.5:1 body text needs.
   - Inputs are 16px in pixels (F2) with a 44px floor (F1) and a --m-fg-muted edge:
     --m-border is 1.24:1, too faint for the boundary of a field you must find.
   ============================================================================= */
.m-close-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.m-close-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.m-close-label {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--m-fg-muted);
}

.m-close-note {
    margin: 0;
    font-size: 0.875rem;
    color: var(--m-fg-muted);
}

.m-close-choices {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.5rem;
}

.m-close-choice {
    min-height: var(--m-touch-min);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    padding: 0.375rem 0.5rem;
    border: 2px solid var(--m-border);
    border-radius: var(--m-radius);
    background: var(--m-surface);
    color: var(--m-fg);
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    cursor: pointer;
}

.m-close-choice > i {
    font-size: 1rem;
    color: var(--m-fg-muted);
}

.m-close-choice[aria-pressed="true"] {
    border-color: var(--m-coral);
    background: var(--m-surface-2);
    font-weight: 700;
}

/* --m-fg, not coral: coral on the selected fill (--m-surface-2) is 2.93:1 in light, under
   the 3:1 an icon needs. The coral edge carries the state; the content stays ink. */
.m-close-choice[aria-pressed="true"] > i {
    color: var(--m-fg);
}

.m-close-choice:disabled {
    cursor: not-allowed;
    opacity: 0.6;
}

.m-close-choice:focus-visible,
.m-close-link:focus-visible,
.m-close-submit:focus-visible {
    outline: 2px solid var(--m-coral);
    outline-offset: 2px;
}

.m-close-tips {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.5rem;
}

.m-close-tip {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.m-close-tip-amount {
    font-size: 0.75rem;
    color: var(--m-fg-muted);
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.m-close-field {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.m-close-field-label {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--m-fg-muted);
}

/* F2: 16px in PIXELS — below 16px iOS zooms the webview on focus and never zooms back. */
.m-close-input {
    width: 100%;
    min-height: var(--m-touch-min);
    box-sizing: border-box;
    padding: 0 0.75rem;
    border: 1px solid var(--m-fg-muted);
    border-radius: var(--m-radius);
    background: var(--m-surface);
    color: var(--m-fg);
    font-family: inherit;
    font-size: 16px;
    font-variant-numeric: tabular-nums;
}

/* #492 - PtStepper's buttons in the waiter screens (mobile close): the field's own line, surface and ink, in the
   waiter tokens .empty-layout defines (light and dark), not the admin's. */
.empty-layout .pt-stepper {
    --pt-stepper-line: var(--m-fg-muted);
    --pt-stepper-inner: var(--m-fg-muted);
    --pt-stepper-weight: 1px;
    --pt-stepper-fill: var(--m-surface);
    --pt-stepper-fill-off: var(--m-surface);
    --pt-stepper-ink: var(--m-fg);
    --pt-stepper-focus: var(--m-fg-muted);
}

.empty-layout .pt-stepper-btn {
    min-height: var(--m-touch-min);
}

/* #492 - the close sheet's coral focus outline goes around the whole box, - and + included, where :has() works; elsewhere
   the field keeps it (.m-close-input:focus below). */
.empty-layout .pt-stepper:has(> .m-close-input:focus) {
    outline: 2px solid var(--m-coral);
    outline-offset: 0;
}

.empty-layout .pt-stepper:has(> .m-close-input:focus) > .m-close-input:focus {
    outline: none;
}

.m-close-input:focus {
    outline: 2px solid var(--m-coral);
    outline-offset: 0;
}

.m-close-hint {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--m-fg-muted);
}

.m-close-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    min-height: 1.5rem;
    font-size: 0.875rem;
    color: var(--m-fg);
    font-variant-numeric: tabular-nums;
}

.m-close-row > span:last-child {
    text-align: right;
}

.m-close-row i {
    width: 1rem;
    color: var(--m-fg-muted);
    text-align: center;
}

.m-close-row-total {
    font-size: 1rem;
    font-weight: 700;
}

.m-close-row-short > span:last-child {
    color: var(--m-danger-fg);
    font-weight: 700;
}

/* "Propina va con": the question on the left, the two answers taking the rest of the row. */
.m-close-row-choice > .m-close-choices {
    flex: 0 1 12rem;
}

.m-close-totals,
.m-close-collect {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--m-border);
}

.m-close-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.75rem;
    border-left: 3px solid var(--m-warn);
    background: var(--m-warn-bg);
    color: var(--m-fg);
    font-size: 0.875rem;
}

.m-close-alert > i {
    flex: 0 0 auto;
    margin-top: 0.125rem;
    font-size: 1rem;
    color: var(--m-warn-fg);
}

.m-close-alert p {
    margin: 0;
}

.m-close-alert-body {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.m-close-alert-danger {
    border-left-color: var(--m-danger);
    background: var(--m-danger-bg);
}

.m-close-alert-danger > i {
    color: var(--m-danger);
}

/* A text action inside an alert: underlined so it reads as one, 44px tall so it can be hit. */
.m-close-link {
    align-self: flex-start;
    min-height: var(--m-touch-min);
    padding: 0;
    border: none;
    background: none;
    color: var(--m-fg);
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 700;
    text-decoration: underline;
    cursor: pointer;
}

.m-close-check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: var(--m-touch-min);
    font-weight: 700;
    cursor: pointer;
}

.m-close-check > input {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin: 0;
    accent-color: var(--m-coral);
}

/* The one CTA the sheet shows at a time: Cerrar cuenta, Listo, or Volver. --m-brand — navy in
   light, coral in dark (the 2026-08-08 ruling). */
.m-close-submit {
    flex: 1 1 auto;
    min-height: var(--m-touch-min);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    border: none;
    border-radius: var(--m-radius);
    background: var(--m-brand);
    color: var(--m-on-brand);
    font-family: inherit;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
}

/* Disabled must be unmistakable on a money action — the waiter has something to do first (confirm
   the held courses, type the cash leg). A dimmed navy still reads as pressable. */
.m-close-submit:disabled {
    background: var(--m-surface-2);
    color: var(--m-fg-muted);
    cursor: not-allowed;
}

.m-close-done {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0 1rem;
    text-align: center;
}

.m-close-done h3 {
    margin: 0;
    font-family: var(--pt-font-logo);
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--m-fg);
}

.m-close-done p {
    margin: 0;
    font-size: 0.875rem;
    color: var(--m-fg);
}

.m-close-done-icon {
    font-size: 2.5rem;
    color: var(--m-positive);
}

.m-close-done-icon.m-close-done-icon-info {
    color: var(--m-fg-muted);
}

.m-close-done-change {
    font-size: 1rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Drawn on white by cfdQrCode, so it keeps its own quiet zone on the dark sheet too. */
.m-close-qr {
    width: 200px;
    height: 200px;
    margin-top: 0.5rem;
    image-rendering: pixelated;
}

.m-close-qr-hint {
    max-width: 18rem;
    color: var(--m-fg-muted);
}

/* ---- Printing from the "Cobrar" sheet (Mesero close parity MC9) ---------------
   "Imprimir cuenta" / "Imprimir recibo" are the sheet's SECOND action, so they are
   outlined in ink rather than filled: the one filled button stays the footer CTA.
   The edge is --m-fg-muted (4.98:1 on the light sheet) because it is the only thing
   that makes the control read as a button. */
.m-close-secondary {
    align-self: stretch;
    min-height: var(--m-touch-min);
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--m-fg-muted);
    border-radius: var(--m-radius);
    background: var(--m-surface);
    color: var(--m-fg);
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
}

.m-close-secondary:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.m-close-secondary:focus-visible {
    outline: 2px solid var(--m-coral);
    outline-offset: 2px;
}

/* The picker is a column of full-width rows — a printer is a name, not a chip. */
.m-close-printers {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.m-close-choice.m-close-printer {
    flex-direction: row;
    justify-content: flex-start;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    text-align: left;
}

.m-close-printer-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Offline is a warning, not a refusal: the job waits for the printer. Amber because the guide
   gives connection warnings to amber (§1.3), in its text tone: --m-warn-fg is 4.67:1 on the
   pressed row's --m-surface-2 in light (--m-warn itself would be 1.96:1). */
.m-close-printer-offline {
    flex: 0 0 auto;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--m-warn-fg);
}

.m-close-print-status {
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--m-fg);
}

.m-close-print-status > i {
    color: var(--m-positive);
}

/* ---- Confirming a guest's claim (Components/Mobile/MobileClaimConfirmSheet.razor, MC4) ----
   The sheet reuses the close sheet's rows, alerts and footer CTA (.m-close-*): the same money
   read at the same moment of service. Only the payer line and the instruction are its own. */
.m-claim-who {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.m-claim-payer {
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: 1rem;
    font-weight: 700;
    color: var(--m-fg);
}

/* The tender pill is the claim card's; on the sheet the row's gap spaces it, not its card margins. */
.m-claim-who .m-td-claim-tender {
    margin: 0;
}

.m-claim-instruction {
    margin: 0;
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    font-size: 0.875rem;
    color: var(--m-fg);
}

.m-claim-instruction > i {
    margin-top: 0.125rem;
    color: var(--m-fg-muted);
}

/* #612 Q2b: the ×1…max count under a chosen option, on the dish's own sheet. Touch-sized steps (44px). */
.m-no-option-count {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.25rem 0 0.5rem;
}

.m-no-option-count-label,
.m-no-option-count-max {
    font-size: 0.8125rem;
    color: var(--m-text-secondary, #6b7280);
}

.m-no-option-count-btn {
    width: 44px;
    height: 44px;
    border: 1.5px solid var(--m-brand);
    border-radius: 0;
    background: var(--m-surface);
    color: var(--m-brand);
    font-size: 1.25rem;
    font-weight: 700;
}

.m-no-option-count-btn:disabled {
    opacity: 0.4;
}

.m-no-option-count-value {
    min-width: 2ch;
    text-align: center;
    font-weight: 700;
}
