/* /Components/Layout/MainLayout.razor.rz.scp.css */
/* App shell: a vertical left sidebar + the page content (sidebar-shell batch,
   matching the Claude-Design dashboard mock). Was a horizontal top header. */
.page[b-ndc6sa3wy0] {
    display: flex;
    flex-direction: row;
    min-height: 100vh;
    font-family: var(--font-ui);
}

/* The nav (.topbar keeps its name for the :has() + collapse hooks) is now a
   sticky, full-height left rail on a light surface — semantic tokens, so it
   flips correctly in dark mode. Collapses to an icon rail (html.nav-collapsed)
   and is hidden entirely on the POS kiosk (:has(.pos-kiosk)). */
.topbar[b-ndc6sa3wy0] {
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100vh;
    width: 240px;
    flex: 0 0 240px;
    z-index: 100;
    background: var(--surface-1);
    border-right: 1px solid var(--border-hair);
    overflow-y: auto;
    padding: var(--space-2);
}

html.nav-collapsed .topbar[b-ndc6sa3wy0] {
    width: 68px;
    flex-basis: 68px;
}

/* POS is a full-screen kiosk: hide the sidebar entirely — the till
   renders its own top bar (shop brand + Menu-size/Photos/Sound + Exit POS). */
html:has(.pos-kiosk) .topbar[b-ndc6sa3wy0] {
    display: none;
}

.content[b-ndc6sa3wy0] {
    flex: 1;
    min-width: 0;
    padding: var(--space-3);
    background: var(--bg-app);
}

/* Kiosk: full-bleed content (the till owns its own padding), but keep bottom
   clearance for the always-present fixed POS dock (Batch B3). */
html:has(.pos-kiosk) .content[b-ndc6sa3wy0] {
    padding: 0 0 3.5rem;
}
/* /Components/Layout/NavMenu.razor.rz.scp.css */
/* Vertical left-sidebar nav (sidebar-shell batch, matching the Claude-Design
   dashboard mock). On a light surface with semantic tokens, so it flips in dark
   mode. Collapses to an icon rail (html.nav-collapsed → 68px). Hidden entirely
   on the POS kiosk (html:has(.pos-kiosk)). */
.nav-menu[b-odrxgeerdq] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    height: 100%;
}

/* Toggle row (collapse + dark mode), above the brand. */
.nav-top[b-odrxgeerdq] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-05);
    padding: 0 var(--space-05);
}

.nav-collapse-toggle[b-odrxgeerdq] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--control-height);
    height: var(--control-height);
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    cursor: pointer;
    flex: 0 0 auto;
}

.nav-collapse-toggle:hover[b-odrxgeerdq] {
    background: var(--bg-sunken);
    color: var(--text-strong);
}

/* Brand: wordmark led by a brass dot (the service bell). */
.brand[b-odrxgeerdq] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0 var(--space-1);
    min-width: 0;
}

.brand-text[b-odrxgeerdq] {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
    line-height: 1.15;
}

.brand strong[b-odrxgeerdq] {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: var(--weight-bold);
    color: var(--text-strong);
    white-space: nowrap;
}

.brand strong[b-odrxgeerdq]::before {
    content: "";
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background: var(--brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
    flex: 0 0 auto;
}

.brand small[b-odrxgeerdq] {
    font-size: var(--fs-caption);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-eyebrow);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Link list — grows to fill and scrolls vertically when it overflows. */
.nav-scroll[b-odrxgeerdq] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: thin;
}

.nav-menu ul[b-odrxgeerdq] {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.nav-menu[b-odrxgeerdq]  a {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--control-height);
    padding: 0 var(--space-1);
    color: var(--text-default);
    text-decoration: none;
    border-radius: var(--radius-sm);
    white-space: nowrap;
}

.nav-menu[b-odrxgeerdq]  a:hover {
    background: var(--bg-sunken);
    color: var(--text-strong);
}

.nav-menu[b-odrxgeerdq]  a.active {
    background: var(--brand-soft);
    color: var(--brand-ink);
    font-weight: var(--weight-semibold);
}

.nav-icon[b-odrxgeerdq] {
    display: inline-flex;
    justify-content: center;
    width: 18px;
    flex: 0 0 auto;
    color: var(--text-muted);
}

.nav-menu[b-odrxgeerdq]  a:hover .nav-icon,
.nav-menu[b-odrxgeerdq]  a.active .nav-icon {
    color: inherit;
}

/* Group label ("Manage"). */
.nav-section[b-odrxgeerdq] {
    margin: var(--space-1) 0 0;
    padding: 0 var(--space-1);
    font-size: var(--fs-caption);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-eyebrow);
    color: var(--text-muted);
    white-space: nowrap;
}

/* Account block, pinned to the foot of the rail. */
.user-block[b-odrxgeerdq] {
    margin-top: auto;
    padding-top: var(--space-2);
    border-top: 1px solid var(--border-hair);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* `--text-muted`, not `--text-faint`: at caption size the faint step measures
   3.33:1 on the light canvas, under AA — and this renders on every admin page. */
.user-email[b-odrxgeerdq] {
    padding: 0 var(--space-1);
    font-size: var(--fs-caption);
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.logout-button[b-odrxgeerdq] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--control-height);
    width: 100%;
    padding: 0 var(--space-1);
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--text-default);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.logout-button:hover[b-odrxgeerdq] {
    background: var(--bg-sunken);
    color: var(--text-strong);
}

/* ---- Collapsed: icon-only rail (manual toggle, or the POS kiosk). ---- */
html.nav-collapsed .nav-label[b-odrxgeerdq],
html:has(.pos-kiosk) .nav-label[b-odrxgeerdq] {
    display: none;
}

html.nav-collapsed .nav-top[b-odrxgeerdq] {
    flex-direction: column;
    gap: var(--space-05);
}

html.nav-collapsed .brand[b-odrxgeerdq] {
    justify-content: center;
}

html.nav-collapsed .nav-section[b-odrxgeerdq] {
    display: none;
}

html.nav-collapsed .nav-menu[b-odrxgeerdq]  a,
html.nav-collapsed .logout-button[b-odrxgeerdq] {
    justify-content: center;
    padding: 0;
    gap: 0;
}
/* /Components/Pages/Client/Branding.razor.rz.scp.css */
.color-picker[b-n1am5ktcfv] {
    padding: 2px;
    height: 40px;
    width: 100px !important;
    max-width: 100px !important;
    cursor: pointer;
}

.file-input[b-n1am5ktcfv] {
    background: var(--bg-sunken);
}

.img-preview[b-n1am5ktcfv] {
    margin-bottom: var(--space-2);
    background: var(--surface-2);
    padding: var(--space-2);
    border-radius: var(--radius-md);
    border: 1px dashed var(--border-strong);
    display: flex;
    align-items: center;
    justify-content: center;
}

.preview-logo[b-n1am5ktcfv] {
    max-height: 100px;
    max-width: 100px;
    object-fit: contain;
}

.preview-favicon[b-n1am5ktcfv] {
    max-height: 32px;
    max-width: 32px;
    object-fit: contain;
}

.preview-banner[b-n1am5ktcfv] {
    max-height: 100px;
    max-width: 300px;
    object-fit: contain;
}

.btn-save[b-n1am5ktcfv] {
    margin-top: var(--space-1);
}
/* /Components/Pages/Client/BusinessInfo.razor.rz.scp.css */
.settings-panel h2[b-35dflhrq6m] {
    margin-top: var(--space-4);
}

.settings-panel h2:first-child[b-35dflhrq6m] {
    margin-top: 0;
}



.btn-save-business-info[b-35dflhrq6m] {
    margin-top: var(--space-2);
}
/* /Components/Pages/Client/CashUp.razor.rz.scp.css */
.cashup[b-n2dlv4v0m7] {
    max-width: 32rem;
}

.cashup-field[b-n2dlv4v0m7] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
    max-width: 14rem;
}

.cashup-takings[b-n2dlv4v0m7],
.cashup-zsummary[b-n2dlv4v0m7] {
    width: 100%;
    border-collapse: collapse;
    margin: var(--space-2) 0;
}

.cashup-takings td[b-n2dlv4v0m7],
.cashup-zsummary td[b-n2dlv4v0m7] {
    padding: var(--space-1) var(--space-2);
    border-bottom: 1px solid var(--border-hair);
}

.cashup-takings td:last-child[b-n2dlv4v0m7],
.cashup-zsummary td:last-child[b-n2dlv4v0m7] {
    text-align: right;
    /* Till-roll numerals: the value column reads as chit figures. */
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

.cashup-takings .indent[b-n2dlv4v0m7] {
    padding-left: var(--space-4);
    color: var(--text-muted);
}

.cashup-drawer[b-n2dlv4v0m7] {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    padding: var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--bg-sunken);
    margin: var(--space-2) 0;
}

.cashup-expected[b-n2dlv4v0m7] {
    font-size: var(--fs-h3);
}

.cashup-movements[b-n2dlv4v0m7] {
    margin: var(--space-1) 0;
    padding-left: var(--space-3);
}

.cashup-movement[b-n2dlv4v0m7] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    align-items: center;
    margin-bottom: var(--space-2);
}

.cashup-mv-amount[b-n2dlv4v0m7],
.cashup-mv-reason[b-n2dlv4v0m7] {
    width: 8rem;
}

.cashup-short[b-n2dlv4v0m7] {
    color: var(--critical-ink);
    font-weight: var(--weight-semibold);
}

.cashup-over[b-n2dlv4v0m7] {
    color: var(--success-ink);
    font-weight: var(--weight-semibold);
}
/* /Components/Pages/Client/Dashboard.razor.rz.scp.css */
.as-of[b-yyrbntdjfm] {
    margin: 0 0 1rem;
    font-size: 0.85rem;
}

/* Page head matching the Claude-Design dashboard mock. */
.dash-head[b-yyrbntdjfm] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
    flex-wrap: wrap;
}

.dash-head-titles h1[b-yyrbntdjfm] {
    margin: 0;
}

.dash-eyebrow[b-yyrbntdjfm] {
    margin: 0 0 var(--space-05);
    font-size: var(--fs-caption);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    font-weight: var(--weight-bold);
    color: var(--text-muted);
}

.dash-head-actions[b-yyrbntdjfm] {
    display: flex;
    gap: var(--space-1);
    flex: 0 0 auto;
}

/* Live-orders card + table (Claude-Design mock). */
.live-orders[b-yyrbntdjfm] {
    margin-top: var(--space-3);
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-1);
    overflow: hidden;
}

.live-head[b-yyrbntdjfm] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border-hair);
}

.live-head h2[b-yyrbntdjfm] {
    margin: 0;
    font-size: var(--fs-h3);
}

.live-orders .table-wrap[b-yyrbntdjfm] {
    overflow-x: auto;
}

.live-orders .data-table[b-yyrbntdjfm] {
    width: 100%;
}

/* Status pills (app.css has no .pill layer, so scope our own from tokens). */
.dash-pill[b-yyrbntdjfm] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-05);
    padding: 2px var(--space-1);
    border-radius: var(--radius-pill);
    font-size: var(--fs-caption);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
}
.dash-pill-info[b-yyrbntdjfm]    { background: var(--brand-soft);      color: var(--brand-ink); }
.dash-pill-fresh[b-yyrbntdjfm]   { background: var(--heat-fresh-tint); color: var(--heat-fresh-ink); }
.dash-pill-warn[b-yyrbntdjfm]    { background: var(--heat-warn-tint);  color: var(--heat-warn-ink); }
.dash-pill-success[b-yyrbntdjfm] { background: var(--grass-tint);      color: var(--grass-ink); }
.dash-pill-neutral[b-yyrbntdjfm] { background: var(--bg-sunken);       color: var(--text-default); }



/* Fullscreen (moved here from the POS ⋯ overflow — you go fullscreen on the way
   INTO the kiosk). Plain .btn chrome; just size the icon like the other heads. */
.dash-fullscreen[b-yyrbntdjfm]  svg {
    width: 16px;
    height: 16px;
}

/* DEV AID — remove with the button. "Simulate call" is dashed + muted so it never
   reads as a real control; it fires a fake inbound call to prove the app-wide
   caller pop-up (CallerPopupHost) reaches a page that isn't the till. */
.dash-sim-call[b-yyrbntdjfm] {
    border-style: dashed;
    color: var(--text-muted);
    font-weight: var(--weight-regular);
}

.dash-sim-call[b-yyrbntdjfm]  svg {
    width: 16px;
    height: 16px;
}
/* /Components/Pages/Client/DeliverySettings.razor.rz.scp.css */
.warn-text[b-mcip55awol] {
    color: var(--warning-ink);
    font-size: 0.9rem;
    margin-top: 0.5rem;
}

.field-row[b-mcip55awol] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 1rem;
}

.threshold-field[b-mcip55awol] {
    margin-top: 0.75rem;
    max-width: 16rem;
}

.area-mode-select[b-mcip55awol] {
    max-width: 28rem;
}

.area-hint[b-mcip55awol] {
    margin-top: 0.75rem;
}

.outcode-adder[b-mcip55awol] {
    margin-top: 0.75rem;
    max-width: 22rem;
}

.outcode-input-row[b-mcip55awol] {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
}

.outcode-input[b-mcip55awol] {
    text-transform: uppercase;
}

.outcode-list[b-mcip55awol] {
    list-style: none;
    margin: 0.75rem 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.outcode-chip[b-mcip55awol] {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: var(--bg-app);
    border: 1px solid var(--border-strong);
    border-radius: 1rem;
    padding: 0.25rem 0.4rem 0.25rem 0.75rem;
    font-variant-numeric: tabular-nums;
}

.outcode-remove[b-mcip55awol] {
    border: none;
    background: var(--border-hair);
    color: var(--text-strong);
    border-radius: 50%;
    width: 1.4rem;
    height: 1.4rem;
    line-height: 1;
    cursor: pointer;
    font-size: 1rem;
}

.outcode-remove:hover:not(:disabled)[b-mcip55awol] {
    background: var(--critical-tint);
}
/* /Components/Pages/Client/DriverAnalysis.razor.rz.scp.css */

p.muted[b-vxg7tit73f] {
    margin-top: 0;
    margin-bottom: var(--space-4);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
}

/* The table itself is the shared `.data-table` (app.css). A private copy used to
   live here — same width/border-collapse/surface/radius/elevation — which beat
   the global rule on specificity and gave this one page full gridlines and a
   bolder header than every other table in the admin. Don't reintroduce it;
   `shared-component-layer-loads-first` records the same trap on Dashboard and
   Kitchen/Shop. */
/* /Components/Pages/Client/Drivers.razor.rz.scp.css */
.driver-form[b-qjnwkpwlfv] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-3) var(--space-2);
    align-items: end;
    background: var(--surface-1);
    padding: var(--space-3);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-hair);
    box-shadow: var(--elev-1);
    margin-bottom: var(--space-4);
}

.form-field[b-qjnwkpwlfv] {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
}

/* A PRIVATE copy of the shared field-label. Scoped CSS carries a [b-xxxxx]
   attribute and therefore beats both global sheets, so fixing `.field label` in
   app.css did NOT reach this page — it kept rendering 1.89:1 in dark mode after
   the other eight were fixed. Same trap the design-system Batch A notes record:
   grep *.razor.css for the family, not just app.css. */
.field-label[b-qjnwkpwlfv] {
    font-size: var(--fs-caption);
    font-weight: var(--weight-bold);
    color: var(--text-default);
}

.form-actions[b-qjnwkpwlfv] {
    display: flex;
    gap: var(--space-1);
    align-items: center;
}

.driver-form input[b-qjnwkpwlfv] {
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font: inherit;
    width: 100%;
}

.driver-form input:focus-visible[b-qjnwkpwlfv] {
    outline: 3px solid var(--focus-ring);
    outline-offset: 1px;
    border-color: var(--accent);
}

.driver-table[b-qjnwkpwlfv] {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface-1);
    border-radius: var(--radius-md);
    overflow: hidden;
    box-shadow: var(--elev-1);
    border-style: hidden;
}

.driver-table th[b-qjnwkpwlfv],
.driver-table td[b-qjnwkpwlfv] {
    text-align: left;
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border-strong);
    font-size: var(--fs-body);
}

.driver-table thead th[b-qjnwkpwlfv] {
    position: sticky;
    top: 0;
    background: var(--bg-sunken);
    font-size: var(--fs-caption);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-strong);
    font-weight: var(--weight-bold);
}

.driver-table tbody tr:hover[b-qjnwkpwlfv] {
    background: var(--slate-50);
}

.driver-table tbody tr.archived[b-qjnwkpwlfv] {
    opacity: 0.6;
    background: var(--bg-sunken);
}

.driver-show-archived[b-qjnwkpwlfv] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-05);
    margin-bottom: var(--space-2);
    font-size: var(--fs-body);
    cursor: pointer;
}
/* /Components/Pages/Client/FindCustomer.razor.rz.scp.css */
.find-customer[b-upumx4ht0n] {
    max-width: 48rem;
}

.fc-search[b-upumx4ht0n] {
    display: flex;
    gap: var(--space-1);
    margin: var(--space-2) 0;
}
.fc-input[b-upumx4ht0n] {
    flex: 1 1 auto;
    min-width: 0;
}

.fc-results[b-upumx4ht0n] {
    width: 100%;
    margin-top: var(--space-2);
}
.fc-results .fc-phone[b-upumx4ht0n] {
    font-variant-numeric: tabular-nums;
}
.fc-results tr.fc-selected[b-upumx4ht0n] {
    background: var(--bg-sunken);
}

.fc-orders[b-upumx4ht0n] {
    margin-top: var(--space-3);
    padding: var(--space-2);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
}
.fc-order-list[b-upumx4ht0n] {
    margin: var(--space-1) 0 0;
    padding-left: var(--space-3);
}

/* Money out from the phone-call flow. Same urgent ink and quiet-link
   treatment as the dock and the orders page: it sits inline in a list of past
   orders, and a filled red button there invites a mis-tap. The confirm guards. */
.fc-refund[b-upumx4ht0n] {
    padding: 0 var(--space-1);
    color: var(--heat-urgent-ink);
    font-size: var(--fs-caption);
    white-space: nowrap;
}

/* An order that has already been refunded says so, rather than just losing its
   button — a control that silently vanishes reads as a bug. */
.fc-refunded[b-upumx4ht0n] {
    margin-left: var(--space-1);
    color: var(--text-muted);
    font-size: var(--fs-caption);
    font-style: italic;
}
/* /Components/Pages/Client/Kitchen/Shop.razor.rz.scp.css */
.shop-header[b-thcf2l2k63] {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

.shop-header h1[b-thcf2l2k63] {
    margin: 0;
    flex: 1 1 auto;
}

.shop-panel[b-thcf2l2k63] {
    /* --surface-1, not the --slate-0 alias: slate-0 is not re-pointed on the
       dark skin, so this panel rendered white under light ink. (Batch E1.) */
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.6rem;
    padding: 1.25rem 1.5rem;
    margin-bottom: 1rem;
}

.shop-panel h2[b-thcf2l2k63] {
    margin: 0 0 0.5rem;
    font-size: 1.1rem;
}

.shop-panel p[b-thcf2l2k63] {
    margin: 0 0 0.75rem;
}

.status-line[b-thcf2l2k63] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

/* Open / paused now use the design system's .pill + .pill-success/.pill-warn
   (Batch A) — the same colours this file hardcoded, plus the glyph. The scoped
   copy that lived here also duplicated the pill shape in raw px. */

.reason-line[b-thcf2l2k63] {
    font-size: 0.95rem;
}

.reason-input[b-thcf2l2k63] {
    width: 100%;
    max-width: 32rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.4rem;
    font: inherit;
    box-sizing: border-box;
}

.prep-row[b-thcf2l2k63] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.prep-input[b-thcf2l2k63] {
    width: 6rem;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.4rem;
    font: inherit;
    text-align: right;
}

/* Shared with the numeric prep-time/warn/urgent inputs, but a 12-hour-clock
   time control (AM/PM) needs more room than 6rem gives it or the AM/PM
   segment clips. */
input[type="time"].prep-input[b-thcf2l2k63] {
    width: 9rem;
    text-align: left;
}
/* /Components/Pages/Client/MenuBuilder.razor.rz.scp.css */
.menu-header[b-lndaab05an] {
    display: flex;
    align-items: baseline;
    gap: 0.85rem;
    margin-bottom: 1.25rem;
    flex-wrap: wrap;
}

.menu-header h1[b-lndaab05an] {
    margin: 0;
}

.publish-actions[b-lndaab05an] {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.confirm-prompt[b-lndaab05an] {
    font-size: 0.9rem;
    color: var(--warning-ink);
}

.version-badge[b-lndaab05an] {
    font-size: 0.85rem;
    color: var(--text-default);
    background: var(--bg-sunken);
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
}

.add-category[b-lndaab05an] {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.add-category input[b-lndaab05an] {
    flex: 0 1 22rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    font: inherit;
}

.category[b-lndaab05an] {
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.6rem;
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
    box-shadow: var(--elev-1);
}

.category.hidden-cat[b-lndaab05an] {
    background: var(--bg-sunken);
    border-style: dashed;
    box-shadow: none;
}

.category-head[b-lndaab05an] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
}

.reorder[b-lndaab05an] {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.icon-btn[b-lndaab05an] {
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: 0.25rem;
    width: 1.5rem;
    height: 1.1rem;
    line-height: 1;
    cursor: pointer;
    font-size: 0.7rem;
    padding: 0;
}

.icon-btn:disabled[b-lndaab05an] {
    opacity: 0.4;
    cursor: default;
}

/* Category image URL + its thumbnail preview. The preview is what tells the operator
   the URL actually resolves — a typo shows as an empty box, not a saved-but-broken
   picture they only discover on the till. */
.cat-image-input[b-lndaab05an] {
    flex: 1 1 14rem;
    min-width: 8rem;
}

.cat-image-preview[b-lndaab05an] {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    object-fit: cover;
    border-radius: 0.375rem;
    border: 1px solid var(--border-hair);
    margin-right: 8px;
    vertical-align: middle;
}

.rename-input[b-lndaab05an] {
    flex: 0 1 18rem;
    font-weight: 600;
}

.pill[b-lndaab05an] {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
}

.hidden-pill[b-lndaab05an] {
    background: var(--warning-tint);
    color: var(--warning-ink);
}

.items[b-lndaab05an] {
    margin-top: 0.5rem;
}

.items .item-name[b-lndaab05an] {
    font-weight: 500;
}

.items .size-count[b-lndaab05an] {
    font-weight: 400;
    color: var(--text-muted);
    font-size: 0.85rem;
    margin-left: 0.4rem;
}

.items .item-price[b-lndaab05an] {
    color: var(--text-default);
    white-space: nowrap;
    width: 7rem;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

.items .item-actions[b-lndaab05an] {
    white-space: nowrap;
    text-align: right;
    width: 18rem;
}

.items .item-actions .btn[b-lndaab05an] {
    margin-left: 0.4rem;
}

.add-item[b-lndaab05an] {
    margin: 0.75rem 0 0;
}

.no-items[b-lndaab05an] {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
}

.avail-ok[b-lndaab05an] { background: var(--success-tint); color: var(--success-ink); }
.avail-soldout[b-lndaab05an] { background: var(--critical-tint); color: var(--critical-ink); }
.avail-hidden[b-lndaab05an] { background: var(--border-hair); color: var(--text-default); }

.muted[b-lndaab05an] {
    color: var(--text-muted);
}

.modifier-groups[b-lndaab05an] {
    margin-top: 2rem;
}

.modifier-groups h2[b-lndaab05an] {
    margin: 0 0 0.25rem;
    font-size: 1.1rem;
}

.add-group[b-lndaab05an] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0.75rem 0 1rem;
}

.add-group input[type="text"][b-lndaab05an],
.add-group > input:not([type])[b-lndaab05an] {
    flex: 0 1 18rem;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    font: inherit;
}

.add-group label[b-lndaab05an] {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.85rem;
    color: var(--text-default);
}

.add-group .num[b-lndaab05an] {
    width: 4rem;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    font: inherit;
}

.add-group select[b-lndaab05an] {
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    font: inherit;
}

.mod-group[b-lndaab05an] {
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.6rem;
    padding: 0.9rem 1.1rem;
    margin-bottom: 0.75rem;
    box-shadow: var(--elev-1);
}

.mod-group-head[b-lndaab05an] {
    margin-bottom: 0.5rem;
}

.no-mods[b-lndaab05an] {
    margin: 0.25rem 0 0.5rem;
    font-size: 0.85rem;
}

.mods .mod-name[b-lndaab05an] {
    font-weight: 500;
}

.mods .mod-price[b-lndaab05an] {
    width: 7rem;
    color: var(--text-default);
    white-space: nowrap;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

.add-mod[b-lndaab05an] {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
}

.add-mod .mod-name-input[b-lndaab05an] {
    flex: 1 1 16rem;
}

.add-mod .mod-price-input[b-lndaab05an] {
    width: 6rem;
}

/* POS Favourites pin toggle + counter (canonical home for favourites curation). */
.fav-counter[b-lndaab05an] {
    margin: 0 0 var(--space-2);
    font-size: var(--fs-body);
}
.item-fav[b-lndaab05an] {
    width: 1%;
    white-space: nowrap;
    text-align: center;
}
.fav-toggle[b-lndaab05an] {
    font-size: 1.15rem;
    line-height: 1;
}
/* A pinned POS Favourite is the "selected" state here — brass (the service
   accent), deliberately NOT the amber that means "cooking" in the semaphore. */
.fav-toggle.is-pinned[b-lndaab05an] {
    border-color: var(--accent);
    color: var(--accent);
    font-weight: var(--weight-semibold);
}
.popular-toggle[b-lndaab05an] {
    font-size: 1.15rem;
    line-height: 1;
}
/* Selected "Popular" is the BRAND ink, not the brass the Favourites star uses:
   the two toggles sit in the same cell and mean different things (till vs
   storefront), so they differ in shape AND colour rather than fill alone. */
.popular-toggle.is-popular[b-lndaab05an] {
    border-color: var(--brand-ink);
    color: var(--brand-ink);
    font-weight: var(--weight-semibold);
}

/* Inline quick-price edit on item rows. */
.price-quickedit[b-lndaab05an] {
    background: none;
    border: 1px dashed transparent;
    border-radius: var(--radius-sm);
    padding: 2px var(--space-1);
    cursor: pointer;
    font: inherit;
    color: var(--text-strong);
}
.price-quickedit:hover:not(:disabled)[b-lndaab05an] {
    border-color: var(--border-strong);
    background: var(--surface-1);
}
.price-edit[b-lndaab05an] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}
.price-input[b-lndaab05an] {
    width: 6rem;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

/* Per-size inline price editing (sized items). */
.size-price-edit[b-lndaab05an] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.size-price-row[b-lndaab05an] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    justify-content: flex-end;
}
.size-price-name[b-lndaab05an] {
    min-width: 4rem;
    text-align: right;
    color: var(--text-muted);
    font-size: var(--fs-caption);
}
.price-edit-actions[b-lndaab05an] {
    display: flex;
    gap: var(--space-1);
    justify-content: flex-end;
}

/* Pizza-building config — category toggles + gated per-item ½&½. */
.cat-pizza-opts[b-lndaab05an] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: var(--space-2);
    font-size: var(--fs-caption);
    color: var(--text-muted);
}
.cat-pizza-opts label[b-lndaab05an] {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
}

/* Per-item ½&½ toggle on item rows (gated by the category). */
.hh-toggle[b-lndaab05an] {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--fs-caption);
    cursor: pointer;
}
.hh-toggle.is-gated[b-lndaab05an] {
    opacity: 0.45;
    cursor: not-allowed;
}
/* /Components/Pages/Client/MenuItemEdit.razor.rz.scp.css */
.allergens[b-tg2jguuzaq] {
    border: 1px solid var(--border-strong);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
    margin: 0 0 1rem;
}

.allergens legend[b-tg2jguuzaq] {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--text-default);
    padding: 0 0.4rem;
}

.allergen-row[b-tg2jguuzaq] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    padding: 0.15rem 0;
}

.sizes-panel[b-tg2jguuzaq] {
    margin-top: 2rem;
    max-width: 32rem;
}

.sizes-panel h2[b-tg2jguuzaq] {
    margin: 0 0 0.5rem;
    font-size: 1.1rem;
}

.sizes-panel .muted[b-tg2jguuzaq] {
    font-size: 0.85rem;
    margin: 0 0 0.75rem;
}

.sizes-panel .sizes[b-tg2jguuzaq] {
    margin: 0.5rem 0;
}

.sizes .size-price[b-tg2jguuzaq] {
    color: var(--text-default);
    white-space: nowrap;
}

.sizes .size-actions[b-tg2jguuzaq] {
    text-align: right;
}

.add-size[b-tg2jguuzaq] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

.add-size .size-name[b-tg2jguuzaq] {
    flex: 1 1 14rem;
    min-width: 12rem;
}

.add-size .size-price-input[b-tg2jguuzaq] {
    width: 6rem;
}

.add-size input[b-tg2jguuzaq] {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    font: inherit;
}

.size-default[b-tg2jguuzaq] {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
}

.mod-groups-panel[b-tg2jguuzaq] {
    margin-top: 2rem;
    max-width: 36rem;
}

.mod-groups-panel h2[b-tg2jguuzaq] {
    margin: 0 0 0.5rem;
    font-size: 1.1rem;
}

.mod-groups-panel .muted[b-tg2jguuzaq] {
    font-size: 0.85rem;
    margin: 0 0 0.75rem;
}

.mods .mod-name[b-tg2jguuzaq] {
    font-weight: 500;
}

.mods .mod-actions[b-tg2jguuzaq] {
    text-align: right;
}

.attach-group[b-tg2jguuzaq] {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

.attach-select[b-tg2jguuzaq] {
    flex: 1 1 18rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    font: inherit;
}
/* /Components/Pages/Client/MenuItemNew.razor.rz.scp.css */
.allergens[b-ryp9qfjoi6] {
    border: 1px solid var(--border-strong);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
    margin: 0 0 1rem;
}

.allergens legend[b-ryp9qfjoi6] {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--text-default);
    padding: 0 0.4rem;
}

.allergen-row[b-ryp9qfjoi6] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    padding: 0.15rem 0;
}
/* /Components/Pages/Client/MyAccount.razor.rz.scp.css */
.settings-panel[b-qkrvsarae7] {
    max-width: 500px;
}

.btn-save[b-qkrvsarae7] {
    margin-top: var(--space-2);
}
/* /Components/Pages/Client/OpeningHours.razor.rz.scp.css */
.hours-table[b-5wt8muhxs3] {
    width: 100%;
    max-width: 36rem;
}

.hours-table .day-col[b-5wt8muhxs3] {
    width: 8rem;
    font-weight: 500;
}

.hours-table .closed-col[b-5wt8muhxs3] {
    width: 5rem;
    text-align: center;
}

.hours-table .time-col[b-5wt8muhxs3] {
    width: auto;
    min-width: 9rem;
}

/* Width only — the box is `.input`'s. This used to restate it without the
   `background`/`color`, so all 14 time fields wore the UA's fill. Invisible on
   paper (the UA's white matches --surface-1) and grey-on-brown in dark. */
.hours-table input[type="time"][b-5wt8muhxs3] {
    min-width: 8.5rem;
}

.hours-table input[type="time"]:disabled[b-5wt8muhxs3] {
    color: var(--text-faint);
    background: var(--bg-app);
}

.hours-table tr.row-closed .day-col[b-5wt8muhxs3] {
    color: var(--text-muted);
}

/* Overnight window indicator (e.g. 13:00–01:00 closes the next day). */
.next-day-hint[b-5wt8muhxs3] {
    margin-left: 0.5rem;
    font-size: var(--fs-caption);
    color: var(--accent);
    white-space: nowrap;
}
/* /Components/Pages/Client/Orders.razor.rz.scp.css */
/* The order book. Filters sit on one wrapping row so a narrow till
   screen stacks them rather than pushing the table sideways
   (`grid-item-min-content-is-a-floor-on-the-whole-page`). */

.orders-filters[b-hlq59p5dal] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    margin-bottom: var(--space-3);
}

/* The search box earns the slack: it is the control this page exists for. */
.orders-search[b-hlq59p5dal] {
    flex: 1 1 22rem;
    min-width: 0;
}

.orders-status[b-hlq59p5dal],
.orders-payment[b-hlq59p5dal] {
    flex: 0 1 12rem;
    min-width: 0;
}

/* A wide table must scroll INSIDE its own box, never widen the page. */
.orders-table[b-hlq59p5dal] {
    width: 100%;
    display: block;
    overflow-x: auto;
}

.orders-paging[b-hlq59p5dal] {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    justify-content: center;
    margin-top: var(--space-3);
}

.orders-page-label[b-hlq59p5dal] {
    font-size: var(--fs-caption);
}

/* Money out — the same urgent ink as the dock's Refund, and the same reasoning:
   a quiet link rather than a filled button, because it sits in a row of
   ordinary data and a solid red button there invites a mis-tap. The confirm is
   the real guard. */
.orders-refund[b-hlq59p5dal] {
    color: var(--heat-urgent-ink);
    font-size: var(--fs-caption);
    white-space: nowrap;
}
/* /Components/Pages/Client/Pos.razor.rz.scp.css */
.visually-hidden[b-9ev9jq3zvc] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---------- till top bar (kiosk chrome): tools + Exit ----------
   The shop-name masthead is gone — the operator knows which shop they're in. */
.pos-topbar[b-9ev9jq3zvc] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    margin-bottom: var(--space-2);
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-1);
}

/* Exit POS — leaves the kiosk back to the app dashboard. */
.pos-exit[b-9ev9jq3zvc] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex: 0 0 auto;
    min-height: 48px;
    padding: 0 var(--space-2);
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: var(--fs-body);
    font-weight: var(--weight-semibold);
    color: var(--text-strong);
    white-space: nowrap;
    cursor: pointer;
}
.pos-exit:hover[b-9ev9jq3zvc] {
    border-color: var(--brand);
    color: var(--brand-ink);
}
.pos-exit[b-9ev9jq3zvc]  svg {
    width: 16px;
    height: 16px;
}

/* ---------- Menu-size + Photos + Sound (+ printer pairing) in the top bar ----------
   No dark toggle here: the sidebar's app-wide theme toggle drives the till too. */
.pos-tools[b-9ev9jq3zvc] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

/* S / M / L segmented control (mirrors the till mockup's .size-seg). The visible
   "Menu size" caption is gone — S/M/L reads on its own and the segment carries an
   aria-label for screen readers. */
.pos-size-seg[b-9ev9jq3zvc] {
    display: inline-flex;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.pos-size-btn[b-9ev9jq3zvc] {
    min-height: 48px;
    width: 44px;
    border: none;
    background: var(--surface-1);
    font: inherit;
    font-size: var(--fs-body);
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
    cursor: pointer;
}

.pos-size-btn + .pos-size-btn[b-9ev9jq3zvc] {
    border-left: 1px solid var(--border-hair);
}

.pos-size-btn[aria-pressed="true"][b-9ev9jq3zvc] {
    background: var(--brand-soft);
    color: var(--brand-ink);
}

/* Photos / Sound per-device toggles. */
.pos-tool[b-9ev9jq3zvc] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 48px;
    padding: 0 var(--space-2);
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: var(--fs-body);
    font-weight: var(--weight-medium);
    color: var(--text-default);
    white-space: nowrap;
    cursor: pointer;
}

.pos-tool:hover[b-9ev9jq3zvc] {
    background: var(--bg-sunken);
}

.pos-tool[aria-pressed="true"][b-9ev9jq3zvc] {
    background: var(--brand-soft);
    border-color: var(--brand-ink);
    color: var(--brand-ink);
}

/* .pos-kitchen-link (retired KDS) and .pos-fullscreen-btn (moved to the
   Dashboard head) are both gone — their chrome went with them. */

/* The "⋯" overflow disclosure is gone: it held only the printer pairing, which
   now sits inline in .pos-tools (it must stay a real click — WebUSB needs the
   gesture), so the menu was an empty box on any browser without WebUSB. */

/* Connect / status for the USB receipt printer (WebUSB) — inline in .pos-tools now
   that the ⋯ overflow is gone. The caller-ID box's connect UI lives in
   Settings › Phone & payments. */
.pos-printer-connect[b-9ev9jq3zvc],
.pos-printer-status[b-9ev9jq3zvc] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 48px;
    padding: 0 var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface-1);
    font: inherit;
    font-size: var(--fs-body);
    font-weight: var(--weight-semibold);
    cursor: pointer;
    white-space: nowrap;
}

.pos-printer-connect:hover[b-9ev9jq3zvc] {
    background: var(--bg-sunken);
}

.pos-printer-status.connected[b-9ev9jq3zvc] {
    border-color: var(--success-bright);
    color: var(--success-ink);
    background: var(--success-tint);
}

/* The "Printer ready" status is informational, not a control. */
.pos-printer-status[disabled][b-9ev9jq3zvc] {
    cursor: default;
}

/* ---- Busy mode ----------------------------------------------
   The button is an ordinary .pos-tool, so it inherits the touch target and the
   aria-pressed "on" treatment — but busy mode is a WARNING state, not a
   preference like Photos or Sound, so the on-state overrides the brand colours
   with the same amber the dock uses for an order that needs attention. It has
   to be legible across the room from behind a counter. */
.pos-busy[b-9ev9jq3zvc] {
    position: relative;
    display: inline-flex;
}

.pos-busy-btn.on[b-9ev9jq3zvc],
.pos-busy-btn[aria-pressed="true"][b-9ev9jq3zvc] {
    background: var(--warning-tint);
    border-color: var(--warning-ink);
    color: var(--warning-ink);
    font-weight: var(--weight-semibold);
}

/* Anchored under the button. It closes on any action or on pressing the button
   again — deliberately NO outside-click listener: posChrome.js holds one handler
   slot per consumer (es-module-is-a-document-singleton) and a third one is not
   worth a browser-only failure mode for a panel with four buttons in it. */
.pos-busy-panel[b-9ev9jq3zvc] {
    position: absolute;
    top: calc(100% + var(--space-1));
    left: 0;
    z-index: 20;
    min-width: 15rem;
    padding: var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface-1);
    box-shadow: var(--elev-2);
}

.pos-busy-state[b-9ev9jq3zvc] {
    margin: 0 0 var(--space-1);
    font-size: var(--fs-body);
    color: var(--text-default);
}

.pos-busy-hint[b-9ev9jq3zvc] {
    margin: var(--space-1) 0 0;
    font-size: var(--fs-caption);
    color: var(--text-muted);
}

.pos-busy-presets[b-9ev9jq3zvc] {
    display: flex;
    gap: var(--space-1);
}

.pos-busy-preset[b-9ev9jq3zvc] {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-height: 48px;
    padding: var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    font: inherit;
    color: var(--text-default);
    cursor: pointer;
}

.pos-busy-preset:hover:not(:disabled)[b-9ev9jq3zvc] {
    background: var(--warning-tint);
    border-color: var(--warning-ink);
}

.pos-busy-preset-add[b-9ev9jq3zvc] {
    font-size: var(--fs-body);
    font-weight: var(--weight-semibold);
}

.pos-busy-preset-total[b-9ev9jq3zvc] {
    font-size: var(--fs-caption);
    color: var(--text-muted);
}

.pos-busy-off[b-9ev9jq3zvc] {
    width: 100%;
    min-height: 44px;
    margin-top: var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    font: inherit;
    font-weight: var(--weight-medium);
    color: var(--text-default);
    cursor: pointer;
}

.pos-busy-off:hover:not(:disabled)[b-9ev9jq3zvc] {
    background: var(--bg-sunken);
}

/* The inbound caller-ID pop-up cards moved to Components/Shared/CallerPopupHost
   (they now show app-wide, not just on the till). Scoped CSS does NOT cross
   component boundaries, so their rules moved WITH them into
   CallerPopupHost.razor.css — don't re-add them here. */

/* Phone-order form inputs use .form-control, which IS defined (app.css) but
   sets no width, so they would otherwise render at the browser's intrinsic
   width and overflow the narrow ticket panel. Constrain them to the panel.
   NOTE (Batch B): the admin pages moved to the design system's `.input`;
   .form-control now survives in app.css FOR THIS FILE and PosBottomDock only,
   because both sit at data-density="touch" where `.input` picks up a 48px
   min-height. Migrate these two and delete the app.css block together in
   Batch E — the class is not dead, whatever an editor's usage count says. */
.phone-order-form .form-control[b-9ev9jq3zvc] {
    width: 100%;
    box-sizing: border-box;
}

/* ---- Phone-order form: compact rows. The eyebrow labels (Type/When/Name/Phone/
   Payment) were removed — the controls speak for themselves (segmented buttons
   read "Collection/Delivery", "ASAP/Pick a time", "Take now/At handover"; the
   name/phone inputs carry placeholders + aria-labels) — so each row is now just a
   full-width control, no label column. ---- */
.of-row[b-9ev9jq3zvc] {
    padding: var(--space-05) 0;
}

.of-field[b-9ev9jq3zvc] {
    min-width: 0;
}
.of-field .form-control[b-9ev9jq3zvc],
.phone-order-form .form-control[b-9ev9jq3zvc] {
    width: 100%;
    box-sizing: border-box;
}

/* Full-width sub-row (delivery address, slot picker) under its row. */
.of-subrow[b-9ev9jq3zvc] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: 0 0 var(--space-1);
}
.of-subrow .phone-delivery-address[b-9ev9jq3zvc] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    border: 0;
    margin: 0;
    padding: 0;
}

/* The day strip and slot grid themselves come from the design system
   (components.css, linked before app.css — `shared-component-layer-loads-first`).
   The till adds only what is local to it: a cap on the grid. A full trading day
   is ~34 slots, which at the 48px touch height is ~600px of buttons — enough to
   push the cart and the Place-order button off the ticket panel on any screen.
   The panel itself scrolls, but the operator should not have to scroll PAST the
   times to reach the order they are building. */
.slot-field .slot-grid[b-9ev9jq3zvc] {
    max-height: 40vh;
    overflow-y: auto;
}

/* Segmented control: buttons or radio-labels that read as a two-up toggle. */
.of-seg[b-9ev9jq3zvc] {
    display: flex;
    gap: var(--control-gap);
    border: 0;
    margin: 0;
    padding: 0;
    min-width: 0;
}
.of-seg > button[b-9ev9jq3zvc],
.of-seg > label[b-9ev9jq3zvc] {
    flex: 1 1 0;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-05);
    min-height: var(--control-height);
    padding: 0 var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    color: var(--text-default);
    font: inherit;
    font-size: var(--fs-body);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    cursor: pointer;
    /* `flex: 1 1 0` shares the row evenly, but a flex item's automatic minimum is
       its content — and these are nowrap — so the longest label set the row's
       floor and pushed the whole segment out of the ticket panel. The pay-timing
       row is the one that bites: "Send pay link — needs phone" overflowed by
       100px once the panel narrowed. Shrink and ellipsize instead; the segment is
       still identifiable from its first words, and the disabled state is carried
       by the control, not the sentence. */
    min-width: 0;
}
.of-seg > button > span[b-9ev9jq3zvc],
.of-seg > label > span[b-9ev9jq3zvc] {
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Hide the underlying radio; the label IS the button. */
.of-seg > label input[b-9ev9jq3zvc] {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}
.of-seg > button.on[b-9ev9jq3zvc],
.of-seg > label:has(input:checked)[b-9ev9jq3zvc] {
    background: var(--brand-soft);
    border-color: var(--brand-ink);
    color: var(--brand-ink);
    font-weight: var(--weight-semibold);
}
.of-seg > label:has(input:disabled)[b-9ev9jq3zvc] {
    opacity: 0.5;
    cursor: not-allowed;
}

.phone-input-row[b-9ev9jq3zvc] {
    display: flex;
    gap: var(--space-1);
}

/* flex-basis drives the prefix size; width:auto cancels the 100% above. */
.phone-customer-prefix[b-9ev9jq3zvc] {
    flex: 0 0 3.5rem;
    width: auto;
    text-align: center;
    padding-left: var(--space-05);
    padding-right: var(--space-05);
}

/* min-width:0 lets the number box shrink below its intrinsic input width. */
.phone-customer-phone[b-9ev9jq3zvc] {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
}

/* A payment option the tenant doesn't offer for this order type — dimmed,
   with an inline reason so the operator knows why it's unavailable. */
.payment-off[b-9ev9jq3zvc] {
    opacity: 0.55;
    cursor: not-allowed;
}
.payment-reason[b-9ev9jq3zvc] {
    font-size: var(--fs-caption);
    color: var(--text-muted);
}

/* ---------- 3-row grid + top rail: item zone | ticket panel ---------- */
.pos-grid[b-9ev9jq3zvc] {
    /* Fallback to silence IDE warnings; dynamically overridden by Pos.razor inline styles */
    --cat-accent: var(--accent);
    display: grid;
    /* Wider ticket column (was 24rem): the panel now carries the whole order form
       AND the ticket, and Park + Checkout share one row at the foot. */
    grid-template-columns: 1fr 30rem;
    grid-template-rows: auto auto 1fr;
    gap: var(--space-2);
    align-items: start;
    /* Paint the work-area background so dark mode (data-theme on this element) covers the
       whole POS — the gaps + the area below the panels — not just the panels themselves.
       In light mode --bg-app == the page colour, so this is a no-op there. */
    background: var(--bg-app);
    min-height: calc(100vh - 1.5rem);
    padding: var(--space-2);
    border-radius: var(--radius-lg);
}

/* The till's device is UNKNOWN — a tenant may run it on a tablet, an all-in-one,
   or a dedicated terminal, so this must not be tuned to any one panel. The ticket
   column is fixed at 30rem, which is right on a 1280-wide screen and takes 44% of
   a 1024-wide one. Below 1200px it narrows so the item grid keeps enough width to
   show more than two products; the two columns stay SIDE BY SIDE at every size,
   because an operator has to see the ticket while adding to it — stacking would
   push it under the fold and is why this is a width change, not a reflow.
   Verify with scripts/pos-viewport-matrix.mjs, never a single size. */
@media (max-width: 1200px) {
    .pos-grid[b-9ev9jq3zvc] { grid-template-columns: 1fr 24rem; }
}

/* Row 2 — the category rail now owns row 1, full width (see PosItemGrid.razor.css). */
.pos-topbar[b-9ev9jq3zvc] {
    grid-column: 1 / 2;
    grid-row: 2 / 3;
    /* Make sure it keeps its layout */
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    /* Grid items default to min-width:auto. The toolbar wraps, but the search
       input still holds an intrinsic floor, which stopped the 1fr column ever
       shrinking below ~572px. */
    min-width: 0;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-lg);
    padding: var(--space-1) var(--space-2);
    box-shadow: var(--elev-1);
}

/* Starts one row lower than before (row 2, not row 1) so the category rail can
   run the full width of the till across the top. */
.ticket-panel[b-9ev9jq3zvc] {
    grid-column: 2 / 3;
    grid-row: 2 / 4;
    display: flex;
    flex-direction: column;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-lg);
    padding: var(--space-2);
    position: sticky;
    top: var(--space-2);
    /* Never taller than the viewport: the line list (.cart-lines) scrolls
       internally so the Checkout / Place-order actions stay on-screen on a
       long ticket. Previously the panel could grow past the fold and bury the
       action row. overflow-y is the backstop for content .cart-lines doesn't
       cover — e.g. the phone-order form (customer + order-type + delivery
       address + slot + payment) is not itself scrollable, so without this the
       Delivery-address fields visually spilled out past the panel's rounded
       border once their combined height exceeded max-height. */
    overflow-y: auto;
    max-height: calc(100vh - var(--space-2) * 2);
    box-shadow: var(--elev-1);
}

.ticket-panel h2[b-9ev9jq3zvc] {
    margin: 0 0 var(--space-1);
    font-size: var(--fs-h3);
}

.alert-success[b-9ev9jq3zvc] {
    background: var(--success-tint);
    color: var(--success-ink);
    border: 1px solid var(--success-tint);
    padding: 0.5rem 0.75rem;
    border-radius: 0.4rem;
    margin-bottom: 0.75rem;
}

.muted[b-9ev9jq3zvc] {
    color: var(--text-muted);
}

/* Meal deals: cart bundle grouping + drawer per-slot modifiers. */
.bundle-slot-mods[b-9ev9jq3zvc] {
    margin-top: 0.5rem;
    padding-left: 0.5rem;
}

/* .drawer-foot is a single flex row (total + Cancel/Add buttons); force the
   validation message onto its own line above that row instead of squeezing in. */
.bundle-draft-error[b-9ev9jq3zvc] {
    flex-basis: 100%;
    order: -1;
    margin-bottom: 0.25rem;
}

/* ---------- type-to-search + parked orders (POS-3) ---------- */
.pos-search-wrap[b-9ev9jq3zvc] {
    position: relative;
    flex: 1 1 auto;
    max-width: 22rem;
    /* An <input> carries an intrinsic width that flex-shrink alone will not go
       below; without this the search box is what stops the toolbar narrowing. */
    min-width: 0;
}

.pos-search[b-9ev9jq3zvc] {
    width: 100%;
    min-height: 48px;
    padding: 0 var(--space-4) 0 var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface-1);
    font: inherit;
    font-size: var(--fs-base);
}

.pos-search-clear[b-9ev9jq3zvc] {
    position: absolute;
    right: var(--space-1);
    top: 50%;
    transform: translateY(-50%);
    border: none;
    background: none;
    cursor: pointer;
    font-size: var(--fs-h2);
    line-height: 1;
    color: var(--text-muted);
}

/* Park + Checkout share one row at the foot of the ticket. Both buttons are
   width:100% by default (.park-order here, .place-order/.place-phone-order in
   app.css), which stacked them; these scoped rules override that. Scoped CSS
   carries an extra attribute selector, so it outranks the global class without
   needing !important (§8 centralise-button-css-globally). */
.ticket-actions[b-9ev9jq3zvc] {
    display: flex;
    align-items: stretch;
    gap: var(--space-1);
    margin-top: var(--space-1);
}

.ticket-actions .park-order[b-9ev9jq3zvc] {
    flex: 1 1 0;
    width: auto;
    min-height: 56px;
    margin-bottom: 0;
}

/* The primary action carries the money, so it gets the larger share. */
.ticket-actions .place-order[b-9ev9jq3zvc],
.ticket-actions .place-phone-order[b-9ev9jq3zvc] {
    flex: 1.5 1 0;
    width: auto;
}

/* ---------- customer recall / repeat order (POS-4) ---------- */
.recall[b-9ev9jq3zvc] {
    margin-bottom: var(--space-2);
}

/* Lives in the ticket head now (opposite Parked), so it's a compact auto-width
   button, not the old full-width one from inside the order form. */
/* 44px, not 40: this is a touch control on a till and it measured 41px — the
   only POS control under the floor `render-check.mjs` enforces. */
.recall-lookup[b-9ev9jq3zvc] {
    flex: 0 0 auto;
    min-height: 44px;
    white-space: nowrap;
}

.recall-note[b-9ev9jq3zvc] {
    margin: var(--space-1) 0 0;
    font-size: var(--fs-body);
    color: var(--warning-ink);
}

.recall-list[b-9ev9jq3zvc] {
    list-style: none;
    margin: var(--space-1) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.recall-item[b-9ev9jq3zvc] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
    background: var(--bg-sunken);
}

.recall-meta[b-9ev9jq3zvc] {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    font-size: var(--fs-body);
}

.recall-number[b-9ev9jq3zvc] {
    font-weight: var(--weight-semibold);
}

.recall-repeat[b-9ev9jq3zvc] {
    flex: 0 0 auto;
    min-height: 44px;
}

/* Backend-degraded banner (B4b): heartbeat can't reach the server. */
/* The connection-degraded banner moved to Components/Shared/ShopPresenceHost
   (it shows on every page now, not just the till) — and its CSS moved with it. */

/* General ("open") item entry. Layout only. */
.general-item[b-9ev9jq3zvc] {
    display: flex;
    gap: 4px;
    margin: 4px 0 8px;
}
.general-item .general-name[b-9ev9jq3zvc] {
    flex: 1 1 auto;
    min-width: 0;
}
.general-item .general-price[b-9ev9jq3zvc] {
    width: 72px;
}


/* Ticket-panel head: Parked (left, where the "Ticket" title used to be) and the
   previous-orders Look up button (right, where Parked used to be). No title. */
.ticket-head[b-9ev9jq3zvc] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
}

/* Quick price-edit. Layout only.

   `display: contents` makes this wrapper vanish from LAYOUT while staying in the
   DOM — so PosItemGrid's two children (.category-rail and .item-zone) become
   direct grid items of .pos-grid and can be placed on their own rows. That is
   what lets the rail span the full width, above the top bar, while the item zone
   stays in column 1. (Their grid-column/grid-row rules already existed in
   PosItemGrid.razor.css but were inert while this wrapper was a flex column.)

   The element must STAY in the DOM: it carries `data-tile` (the S/M/L tile scale,
   read as an ancestor by PosItemGrid's scoped rules) and the `photos` class (gates
   the fill-the-tile item photos). display:contents removes its BOX, not the node, so
   descendant/ancestor matching + inheritance still work through it. */
.item-zone-wrap[b-9ev9jq3zvc] {
    display: contents;
}

/* ---------- Menu size (S / M / L) ----------
   The setting is a TILE SIZE, not a column count. Each density supplies a minimum
   tile width plus the tile's height and type scale; PosItemGrid's .item-grid feeds
   the minimum to `repeat(auto-fill, minmax(…, 1fr))`, so the browser fits as many
   columns as the screen allows and 1fr spends the remainder — the grid always ends
   exactly at the ticket column, with no dead space and no billboard tiles.

   Consequence worth knowing: the column count now varies with the viewport. On a
   given till that is constant (the screen never changes), so item positions are as
   stable as they ever were; they differ only across differently-sized screens and
   on a fullscreen toggle. That is the deliberate trade for a tile that stays the
   same comfortable size everywhere instead of swinging 109px → 280px with the
   screen, which is what the old fixed 7/5/3 columns did.

   These custom properties cross into PosItemGrid's scoped CSS because custom
   properties inherit through the DOM — CSS isolation rewrites selectors, not
   inherited values (§8 css-vars-pierce-isolation) — and `display: contents` above
   does not interrupt inheritance. */
.item-zone-wrap[data-tile="compact"][b-9ev9jq3zvc] {
    --pos-tile-min: 150px;
    --pos-tile-h: 64px;
    --pos-tile-pad: var(--space-1) var(--space-2);
    --pos-tile-fs: 0.95rem;
    --pos-tile-price-fs: 0.8rem;
}

.item-zone-wrap[data-tile="regular"][b-9ev9jq3zvc] {
    --pos-tile-min: 200px;
    --pos-tile-h: 88px;
    --pos-tile-pad: 12px var(--space-2);
    --pos-tile-fs: 1.05rem;
    --pos-tile-price-fs: 0.85rem;
}

/* L centres its label: the tile is wide enough that a short name ("Chips")
   left-aligned in it looks stranded. S and M stay left-aligned — at their width
   the names vary in length, and a ragged left edge is slower to scan. */
.item-zone-wrap[data-tile="large"][b-9ev9jq3zvc] {
    --pos-tile-min: 280px;
    --pos-tile-h: 120px;
    --pos-tile-pad: var(--space-2) 20px;
    --pos-tile-fs: 1.25rem;
    --pos-tile-price-fs: 0.95rem;
    --pos-tile-align: center;
    --pos-tile-items: center;
}

/* Menu-size (S/M/L), Photos, Sound and Dark all live in the top bar; their chrome
   comes from .pos-size-seg / .pos-tool near the top of this file. The dark toggle
   carries no bespoke rules any more — it is a .pos-tool like Photos and Sound, so
   it picks up the same aria-pressed "on" state. */

/* ---- Customer chip: the two-line summary card that replaced the
   inline name/phone/address rows — tap opens the customer drawer. Line 1 =
   name + phone (confirm-back data); line 2 (delivery) = street + BOLD
   postcode, the thing drivers and staff key on. ---- */
.customer-chip[b-9ev9jq3zvc] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-05);
    width: 100%;
    min-height: var(--control-height);
    padding: var(--space-05) var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    color: var(--text-default);
    font: inherit;
    font-size: var(--fs-body);
    text-align: left;
    cursor: pointer;
}
.customer-chip.chip-empty[b-9ev9jq3zvc] { color: var(--text-muted); }
.customer-chip.chip-needed[b-9ev9jq3zvc] {
    border-color: var(--warning-ink);
    color: var(--warning-ink);
}
.customer-chip .chip-line[b-9ev9jq3zvc] {
    display: flex;
    align-items: baseline;
    gap: var(--space-1);
    min-width: 0;
}
.customer-chip .chip-name[b-9ev9jq3zvc],
.customer-chip .chip-street[b-9ev9jq3zvc] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.customer-chip .chip-phone[b-9ev9jq3zvc] { margin-left: auto; }
.customer-chip .chip-edit[b-9ev9jq3zvc] { color: var(--text-muted); }
.customer-chip .chip-postcode[b-9ev9jq3zvc] {
    margin-left: auto;
    font-weight: var(--weight-semibold);
    font-size: var(--fs-base);
    white-space: nowrap;
}
.customer-chip .chip-notes-dot[b-9ev9jq3zvc] { color: var(--warning-ink); }

/* The customer drawer reuses the shared .line-drawer shell (global app.css);
   only its field spacing is local. */
.customer-drawer .drawer-body[b-9ev9jq3zvc] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.customer-drawer .phone-delivery-address[b-9ev9jq3zvc] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    border: 0;
    margin: 0;
    padding: 0;
}

/* Collapsed time picker. Mirrors `.customer-chip` directly below it — same
   full-width tap target, same border and surface — because it is the same idea:
   a finished choice folded down to one line so it stops holding the column open.
   Row rather than column: this summarises ONE fact ("Tomorrow 4 Aug · 11:45"),
   where the customer chip stacks two. */
.slot-chip[b-9ev9jq3zvc] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    width: 100%;
    min-height: var(--control-height);
    padding: var(--space-05) var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    color: var(--text-default);
    font: inherit;
    font-size: var(--fs-body);
    text-align: left;
}

.slot-chip .slot-chip-value[b-9ev9jq3zvc] {
    font-weight: 600;
    min-width: 0;
}

.slot-chip .slot-chip-edit[b-9ev9jq3zvc] {
    flex: 0 0 auto;
    color: var(--text-muted);
}

/* "couldn't read this shop's rules" strip.
   LAYOUT ONLY: the colours come from the design system's .alert-warning, so this
   cannot drift out of contrast and needs no token change. It spans both grid
   columns because a warning that sits over one panel reads as being about that
   panel, and this one is about the whole till. */
.pos-rules-warning[b-9ev9jq3zvc] {
    grid-column: 1 / -1;
    margin: 0;
}
/* /Components/Pages/Client/PosCartLines.razor.rz.scp.css */
/* Styles for the POS cart line list, co-located with PosCartLines (moved from
   Pos.razor.css in Batch 155 — scoped CSS only matches its own component). */

.empty-cart[b-861znyis0r] {
    margin: 0.75rem 0;
    font-size: 0.9rem;
}

.cart-lines[b-861znyis0r] {
    list-style: none;
    padding: 0;
    margin: 0 0 0.75rem;
    /* Grow to fill the panel and scroll internally (min-height:0 lets a flex
       child shrink below content size so the overflow actually scrolls). */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.cart-line[b-861znyis0r] {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--bg-sunken);
}

.cart-line:last-child[b-861znyis0r] { border-bottom: none; }

.cart-line-head[b-861znyis0r] {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.25rem;
}

.line-name[b-861znyis0r] { font-weight: 500; flex: 1 1 auto; }
.line-size[b-861znyis0r] { font-size: 0.85rem; }

.line-remove[b-861znyis0r] {
    background: none;
    border: none;
    color: var(--text-faint);
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0 0.25rem;
}

.line-remove:hover[b-861znyis0r] { color: var(--critical-ink); }

.cart-line-foot[b-861znyis0r] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.qty-controls[b-861znyis0r] {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.qty-btn[b-861znyis0r] {
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    width: 44px;
    height: 44px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-h3);
    font-weight: var(--weight-bold);
    line-height: 1;
}

.qty[b-861znyis0r] {
    min-width: 1.5rem;
    text-align: center;
    font-weight: 600;
}

.line-total[b-861znyis0r] {
    font-weight: var(--weight-semibold);
    color: var(--text-strong);
}

/* Totals row — reads like the till mockup's prominent Total:
   a strong top border and a big mono figure. */
.cart-subtotal[b-861znyis0r] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: var(--space-2) 0 var(--space-1);
    border-top: 1px solid var(--border-strong);
    margin-bottom: var(--space-1);
    font-size: var(--fs-h2);
    font-weight: var(--weight-bold);
    color: var(--text-strong);
}

.line-mods[b-861znyis0r],
.line-instructions[b-861znyis0r] {
    margin: 0.15rem 0 0.15rem 0.25rem;
    font-size: 0.85rem;
    color: var(--text-default);
}

.line-instructions[b-861znyis0r] {
    font-style: italic;
}

/* Cart bundle grouping. */
.cart-bundle .bundle-components[b-861znyis0r] {
    list-style: none;
    margin: 0.25rem 0 0 0.75rem;
    padding-left: 0.6rem;
    border-left: 2px solid var(--border-hair);
    font-size: 0.92em;
}

.bundle-name[b-861znyis0r] {
    font-weight: 700;
}

/* Till line-op controls. Layout only — colours come from .btn tokens. */
.line-adjust[b-861znyis0r] {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    align-items: center;
    margin-top: 4px;
}
.line-adjust .adj-btn[b-861znyis0r] {
    font-size: 12px;
    padding: 2px 6px;
}
.line-adjust .adj-input[b-861znyis0r] {
    width: 52px;
}
.line-was[b-861znyis0r] {
    opacity: 0.6;
    margin-right: 6px;
}
/* /Components/Pages/Client/PosCheckoutModal.razor.rz.scp.css */
/* Cash tender pad (POS-2): change calculator for walk-in cash. Modal-only, so
   it lives in this component's scoped sheet (moved from Pos.razor.css, Batch 157).
   The drawer frame (.line-drawer/.drawer-*) and the shared .payment-method are
   global. */
.tender[b-7iviy1il8g] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
}

.tender-summary[b-7iviy1il8g] {
    background: var(--bg-sunken);
    border-radius: var(--radius-md);
    padding: var(--space-1) var(--space-2);
}

.tender-row[b-7iviy1il8g] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: var(--space-05) 0;
}

.tender-row strong[b-7iviy1il8g] {
    font-size: var(--fs-h3);
}

.tender-change[b-7iviy1il8g] {
    color: var(--success-ink);
}

.tender-change strong[b-7iviy1il8g] {
    color: var(--success-500);
    font-size: var(--fs-h2);
}

.tender-short[b-7iviy1il8g] {
    color: var(--critical-ink);
}

.tender-short strong[b-7iviy1il8g] {
    color: var(--status-critical);
}

.quick-cash[b-7iviy1il8g] {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-1);
}

.cash-chip[b-7iviy1il8g] {
    min-height: 48px;
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: var(--radius-md);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-base);
    font-weight: var(--weight-semibold);
}

.cash-chip:active[b-7iviy1il8g] {
    transform: translateY(1px);
    background: var(--bg-app);
}

.keypad[b-7iviy1il8g] {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-1);
}

.keypad-key[b-7iviy1il8g] {
    min-height: 56px;
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: var(--radius-md);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-h2);
    font-weight: var(--weight-semibold);
    line-height: 1;
}

.keypad-key:active[b-7iviy1il8g] {
    transform: translateY(1px);
    background: var(--bg-app);
}

.keypad-clear[b-7iviy1il8g] {
    color: var(--status-critical);
}

.keypad-back[b-7iviy1il8g] {
    color: var(--text-muted);
}
/* /Components/Pages/Client/PosItemGrid.razor.rz.scp.css */
/* Styles for the POS category rail + item zone, co-located with PosItemGrid
   (extracted from Pos.razor.css in Batch 154). Scoped CSS only rewrites this
   component's own elements, so these rules MUST live with the markup that
   renders them. Grid placement (grid-column/grid-row) still resolves against
   the parent `.pos-grid` container — grid is DOM-hierarchy based, not scope
   based — and `--cat-accent` inherits down from `.pos-grid` across the scope
   boundary (custom properties aren't sandboxed by CSS isolation). */

/* ---------- top: category rail (fixed order, colour-coded, touch) ----------
   Row 1 of .pos-grid, spanning the FULL till width (over the ticket column too),
   so the whole menu is reachable in one tap.

   ONE ROW, ALWAYS. It never wraps to a second line and never scrolls sideways:
   the tabs keep their natural width while the rail fits, and shrink to fit when
   it doesn't (see .cat-tab). Every category is therefore visible and in a fixed
   position on any screen — a smaller screen makes the tabs smaller, it does not
   hide them or move them to another line. */
.category-rail[b-7wuu02ntea] {
    grid-column: 1 / 3;
    grid-row: 1 / 2;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: var(--space-1);
    padding-bottom: var(--space-05);
    position: sticky;
    top: var(--space-2);
    z-index: 10;
    /* A grid item defaults to min-width:auto, and this one is nowrap and spans
       BOTH columns — so its min-content (every tab, side by side) was a floor on
       the width of the whole till. With 13 categories that floor is ~1040px, and
       below it the entire POS overflowed horizontally: on a 1024×768 screen the
       Checkout button sat off the right edge. The tabs already shrink to a
       minimum; past that the rail scrolls rather than pushing the page wider.
       (The tenant's device is unknown — see docs/design-system-migration-plan.md.) */
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: thin;
}

.cat-tab[b-7wuu02ntea] {
    /* Positioned + clipped so an illustrated tab can stack photo → scrim → label
       inside it (see .has-image below). */
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: var(--radius-md);
    min-height: 60px; /* Taller cell: cleaner, and shows more of an illustrated tab's photo. */
    padding: 0 var(--space-2);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-base);
    font-weight: var(--weight-medium);
    text-align: left;
    /* GROW to fill the row when there is spare space, but NEVER shrink below the
       label's own width — the rail scrolls instead.

       This reverses half of the original rule, deliberately. The old
       `flex: 1 1 auto` + `min-width: 0` let tabs compress without limit so that
       every category stayed on screen at a fixed position; measured on Spring
       Cottage's own 1280×1024 panel with 14 categories, that produced 66px tabs
       and clipped 13 of the 14 labels — "Pizzas" got 32px of the 49 it needed,
       and the rail read `Pi… Ke… Bu… Chi… D… De…`, with Drinks and Desserts
       reduced to `D…` and `De…` side by side. An operator cannot pick from that,
       so the "always visible" property was buying nothing.

       `flex-shrink: 0` makes each tab's content width its floor. The rail is
       already a scroll container (`min-width: 0` + `overflow-x: auto` on
       .category-rail — the fix for
       `grid-item-min-content-is-a-floor-on-the-whole-page`), so this canNOT
       re-floor the page width; it just means the far categories are now reached
       by scrolling. That was already the documented fallback below ~1040px —
       with 14 categories the threshold is simply higher than it looked.

       .cat-label keeps its ellipsis as a backstop for a pathologically long
       category name. */
    flex: 1 0 auto;
    white-space: nowrap;
    box-shadow: var(--elev-1);
    transition: background var(--motion-fast) var(--ease),
                border-color var(--motion-fast) var(--ease),
                box-shadow var(--motion-fast) var(--ease),
                transform var(--motion-fast) var(--ease);
}

.cat-tab:hover:not(.active)[b-7wuu02ntea] {
    background: var(--bg-app);
    box-shadow: var(--elev-2);
    transform: translateY(-1px);
}

.cat-chip[b-7wuu02ntea] {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-pill);
    background: var(--cat-accent, var(--accent));
}

/* ---------- illustrated category tab ----------
   The photo FILLS the tab and the label sits on top, which reads faster than a small
   thumbnail — you recognise a picture before you read a word — and costs no extra
   height, so the rail stays one row at 56px.

   Three layers inside the tab: .cat-bg (z 0) → the ::after scrim (z 1) → .cat-label
   (z 2). The scrim is NOT decoration: a food photo has arbitrary brightness, so bold
   white text laid straight on it disappears over a pale pizza. The gradient starts in
   the category's own accent (so the colour-coding the operator navigates by survives)
   and fades toward the photo, guaranteeing contrast under the label no matter what the
   image does. Same discipline as §8's --on-brand rule: never trust a colour you don't
   control to carry text. */
.cat-tab.has-image[b-7wuu02ntea] {
    border-color: transparent;
    background: var(--cat-accent, var(--accent));
}

.cat-bg[b-7wuu02ntea] {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* The accent shows through while the photo loads, and stays if the URL 404s — a
       dead link reads as a coloured tab, never a broken-image glyph. */
    background: var(--cat-accent, var(--accent));
}

/* A PHOTO-FORWARD readability scrim: a plain dark wash, heaviest behind the
   left-aligned label and fading to nearly clear on the right, so most of the tab
   is the food photo. The earlier version tinted the left ~92% in the category
   accent — near-opaque, which buried the picture; the accent no longer rides the
   scrim (on an illustrated tab the photo IS the recognition cue, and the tab's
   accent background still shows while the image loads / if the URL 404s).

   Darkened with --paper-900 and lettered in --paper-0 rather than raw black/white
   literals: scoped CSS must go through the design tokens (enforced by
   BlazorScopedCssUsesDesignTokensTests — which scans the raw file, comments included,
   so don't write a hex literal even in prose here). Both are PRIMITIVES — the dark
   theme re-points the semantic tokens onto them but never redefines them — so the
   scrim stays dark and the label stays white in both skins: the label must not
   follow the theme, it must fight the picture. 68% behind the label keeps white
   text ≥4.5:1 even over a pale image; the text-shadow is the backstop. */
.cat-tab.has-image[b-7wuu02ntea]::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
        90deg,
        color-mix(in srgb, var(--paper-900) 68%, transparent) 0%,
        color-mix(in srgb, var(--paper-900) 30%, transparent) 48%,
        color-mix(in srgb, var(--paper-900) 6%, transparent) 100%);
}

.cat-tab.has-image .cat-label[b-7wuu02ntea] {
    position: relative;
    z-index: 2;
    color: var(--paper-0);
    font-weight: var(--weight-bold);
    text-shadow: 0 1px 2px color-mix(in srgb, var(--paper-900) 60%, transparent);
}

/* The plain tab's active state (inset accent bar + paper background) is invisible on a
   photo, so an illustrated tab shows selection as a ring instead. */
.cat-tab.has-image.active[b-7wuu02ntea] {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand), var(--elev-2);
    background: var(--cat-accent, var(--accent));
}

.cat-tab.has-image:hover:not(.active)[b-7wuu02ntea] {
    transform: translateY(-1px);
    box-shadow: var(--elev-2);
}

/* When the rail has to squeeze, the LABEL is what gives — it ellipses inside the
   tab rather than the tab overflowing the row. The colour chip is flex: 0 0 auto
   above, so it never shrinks: even a heavily truncated tab keeps its category
   colour, which is what the operator navigates by. */
.cat-label[b-7wuu02ntea] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ★ Favourites tab: the chip carries the star glyph instead of a colour dot.
   MUST live here (not Pos.razor.css) — .cat-chip is this component's scoped element. */
.cat-tab-fav[b-7wuu02ntea] {
    --cat-accent: var(--accent);
}
.cat-tab-fav .cat-chip[b-7wuu02ntea] {
    width: auto;
    height: auto;
    background: transparent;
    border-radius: 0;
    font-size: var(--fs-base);
    line-height: 1;
    color: var(--accent);
}

/* Favourites keeps its ★ even over its photo (unlike a normal category, which drops
   its dot when illustrated) — the star is what tells it apart from any other food
   tab. Lift it above the scrim (z 1) and letter it white to match the label. */
.cat-tab-fav.has-image .cat-chip[b-7wuu02ntea] {
    position: relative;
    z-index: 2;
    color: var(--paper-0);
}

.cat-label[b-7wuu02ntea] {
    flex: 1 1 auto;
}

.cat-tab.active[b-7wuu02ntea] {
    border-color: var(--cat-accent, var(--accent));
    box-shadow: inset 4px 0 0 0 var(--cat-accent, var(--accent)), var(--elev-1);
    background: var(--bg-app);
    font-weight: var(--weight-semibold);
    color: var(--text-strong);
}

/* ---------- middle: item zone ---------- */
.item-zone[b-7wuu02ntea] {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    /* A grid item defaults to min-width:auto, which lets a wide item grid push
       the column past its 1fr track. .item-zone-wrap used to carry this; now that
       the wrapper is display:contents, the zone is the grid item and owns it. */
    min-width: 0;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-top: 3px solid var(--cat-accent, var(--accent));
    border-radius: var(--radius-lg);
    padding: var(--space-2);
    box-shadow: var(--elev-1);
    /* All items of a category live on ONE page now (no pager) — the zone
       scrolls vertically when the category overflows the till height. */
    max-height: calc(100vh - 12rem);
    overflow-y: auto;
}

/* The MENU SIZE (S/M/L) picks a minimum tile width; the browser fits as many
   columns as the screen allows and `1fr` spends what's left, so the grid always
   ends flush with the ticket column — no dead space on a big screen, and no
   cramped 109px tile on a small one. The old `repeat(7|5|3, 1fr)` pinned the
   column count instead, which let the tile swing 2.5x across till sizes.

   auto-FILL, not auto-fit: auto-fit collapses empty tracks, which would stretch
   the tiles of a short category to fill the row. auto-fill keeps the tile size
   the operator chose. --pos-tile-* come from .item-zone-wrap[data-tile] (Pos.razor.css). */
.item-grid[b-7wuu02ntea] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--pos-tile-min, 200px), 1fr));
    gap: var(--space-1);
}

.item-card[b-7wuu02ntea] {
    /* Positioned + clipped so a photo tile can stack image → scrim → label inside it
       (see .photos .item-card.has-photo below), same as a category tab. */
    position: relative;
    overflow: hidden;
    /* Use a 15% tint of the category accent so it looks obviously colour-coded but remains readable */
    background: color-mix(in srgb, var(--cat-accent, var(--surface-1)) 15%, var(--surface-1));
    border: 1px solid color-mix(in srgb, var(--cat-accent, var(--border-strong)) 40%, transparent);
    border-left: 4px solid var(--cat-accent, var(--border-strong));
    border-radius: var(--radius-md);
    /* Height, padding, type scale and alignment all follow the menu-size setting
       (S/M/L) — NOT the screen. The operator chooses how big a button is; the
       screen only decides how many of them fit. Defaults here are the M values,
       used if the wrapper ever renders without a data-tile. */
    min-height: var(--pos-tile-h, 88px);
    padding: var(--pos-tile-pad, 12px var(--space-2));
    cursor: pointer;
    text-align: var(--pos-tile-align, left);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: var(--pos-tile-items, stretch);
    gap: var(--space-05);
    font: inherit;
    font-size: var(--pos-tile-fs, 1.05rem);
    font-weight: var(--weight-medium);
    transition: background var(--motion-fast) var(--ease),
                box-shadow var(--motion-fast) var(--ease),
                transform var(--motion-fast) var(--ease);
}

.item-card:hover:not(:disabled)[b-7wuu02ntea] {
    /* Deepen the colour on hover */
    background: color-mix(in srgb, var(--cat-accent, var(--bg-app)) 25%, var(--bg-app));
    border-color: var(--cat-accent);
    box-shadow: var(--elev-1);
}

.item-card:active:not(:disabled)[b-7wuu02ntea] {
    box-shadow: none;
    transform: translateY(1px);
}

.item-card:disabled[b-7wuu02ntea] {
    opacity: 0.5;
    cursor: default;
}

.item-card-name[b-7wuu02ntea] {
    font-weight: var(--weight-semibold);
    font-size: inherit;
    color: var(--text-strong);
}

/* Optional price under the item name — toggled per-tenant. */
.item-card-price[b-7wuu02ntea] {
    color: var(--text-default);
    /* Scales with the menu-size setting alongside the item name. */
    font-size: var(--pos-tile-price-fs, var(--fs-caption));
}

/* Half-and-half bar. Layout only. */
.half-half-bar[b-7wuu02ntea] {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.half-half-status[b-7wuu02ntea] {
    font-size: var(--fs-body);
    color: var(--text-muted);
}

/* An item photo FILLS its tile (like a category tab), gated by Photos being on.
   Hidden by default — off, or an item with no picture, keeps the plain colour tile.
   An <img>, never a CSS background-image (a user URL there is an injection vector,
   §8 user-url-goes-in-an-img-src-never-a-css-url). */
.item-card-thumb[b-7wuu02ntea] {
    display: none;
}

.photos .item-card.has-photo .item-card-thumb[b-7wuu02ntea] {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The name/price sit at the BOTTOM over a dark-at-the-bottom scrim, so a bold white
   label reads over any photo (§8 discipline: never trust a colour you don't control
   to carry text). Darkened with --paper-900 / lettered in --paper-0 — primitives, so
   it holds in both themes; no raw hex (BlazorScopedCssUsesDesignTokensTests). */
.photos .item-card.has-photo[b-7wuu02ntea] {
    justify-content: flex-end;
    border-color: transparent;
    background: var(--cat-accent, var(--surface-1));
}

.photos .item-card.has-photo[b-7wuu02ntea]::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
        180deg,
        color-mix(in srgb, var(--paper-900) 6%, transparent) 35%,
        color-mix(in srgb, var(--paper-900) 74%, transparent) 100%);
}

.photos .item-card.has-photo .item-card-name[b-7wuu02ntea],
.photos .item-card.has-photo .item-card-price[b-7wuu02ntea] {
    position: relative;
    z-index: 2;
    color: var(--paper-0);
    text-shadow: 0 1px 2px color-mix(in srgb, var(--paper-900) 60%, transparent);
}

.photos .item-card.has-photo .item-card-name[b-7wuu02ntea] {
    font-weight: var(--weight-bold);
}

/* ½&½ entry tile + ineligible fade in ½&½ mode. */
.half-half-tile[b-7wuu02ntea] {
    --cat-accent: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-1));
    border-style: dashed;
    align-items: center;
    text-align: center;
}
.half-half-tile.active[b-7wuu02ntea] {
    background: var(--accent);
    /* Text ON the brand is --on-brand, not a raw white alias. Identical here
       (petrol takes white) but correct if the slot ever carries a lighter
       colour, where white would fail. (Batch E1.) */
    color: var(--on-brand);
    border-style: solid;
}
.half-half-tile.active .item-card-name[b-7wuu02ntea],
.half-half-tile.active .item-card-price[b-7wuu02ntea] {
    color: inherit;
}
.item-card.is-ineligible[b-7wuu02ntea] {
    opacity: 0.4;
}
/* /Components/Pages/Client/PosParkedControl.razor.rz.scp.css */
/* Parked-orders control, co-located with PosParkedControl (moved from
   Pos.razor.css, Batch 158). */
.parked-control[b-68m0i2lw83] {
    position: relative;
    /* No margin-left:auto — Parked sits at the LEFT of the ticket head now (where
       the "Ticket" title was); the head's space-between pushes Look up to the right. */
}

.parked-toggle[b-68m0i2lw83] {
    min-height: 48px;
    padding: 0 var(--space-2);
    border: 1px solid var(--border-strong);
    background: var(--surface-1);
    border-radius: var(--radius-md);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-base);
    font-weight: var(--weight-semibold);
}

.parked-toggle.active[b-68m0i2lw83] {
    background: var(--bg-app);
    border-color: var(--accent);
}

.parked-panel[b-68m0i2lw83] {
    position: absolute;
    right: 0;
    top: calc(100% + var(--space-1));
    z-index: 12;
    width: 22rem;
    max-width: 90vw;
    background: var(--surface-2);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-3);
    padding: var(--space-2);
}

.parked-list[b-68m0i2lw83] {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.parked-item[b-68m0i2lw83] {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.parked-label[b-68m0i2lw83] {
    flex: 1 1 auto;
    font-weight: var(--weight-medium);
}

.parked-discard[b-68m0i2lw83] {
    border: none;
    background: none;
    cursor: pointer;
    color: var(--text-faint);
    font-size: var(--fs-h3);
    line-height: 1;
    padding: 0 var(--space-05);
}

.parked-discard:hover[b-68m0i2lw83] {
    color: var(--critical-ink);
}
/* /Components/Pages/Client/PosPayLinkFallback.razor.rz.scp.css */
/* Pay-link SMS-failure fallback. Tokens only (arch test scans raw hex). */
.phone-order-paylink-failed[b-4fk7kcpld2] {
    color: var(--warning-ink);
    font-weight: 600;
    margin: 0.25rem 0 0;
}

/* `.phone-order-paylink-url` is GONE with the URL itself. A live Stripe Checkout
   URL is ~500 characters — it can't be read down a phone, and printing it pushed
   the actions off the bottom of a 1024px till. Don't reintroduce either. */

.phone-order-paylink-fix[b-4fk7kcpld2] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.phone-order-paylink-fix .paylink-fix-phone[b-4fk7kcpld2] {
    flex: 1 1 10rem;
    min-width: 0;
    min-height: 44px;
}

.phone-order-paylink-fix .btn[b-4fk7kcpld2] {
    min-height: 44px;
}

/* P2 switch-tender actions. Wraps rather than shrinks: this panel sits in the
   ticket column, and a button that shrinks below the touch floor on a narrow
   till is worse than one that takes a second row. min-height matches the 44px
   floor render-check.mjs enforces. */
.phone-order-paylink-actions[b-4fk7kcpld2] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.phone-order-paylink-actions .btn[b-4fk7kcpld2] {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 44px;
}
/* /Components/Pages/Client/PosSettings.razor.rz.scp.css */
/* ---- Caller-ID devices panel (A3b-2) ---- */
.device-create[b-ggvgd6i2jx] {
    display: flex;
    gap: var(--space-1);
    align-items: center;
    margin: var(--space-3) 0;
}

.device-name-input[b-ggvgd6i2jx] {
    flex: 1 1 auto;
    min-width: 0;
}

.device-create-btn[b-ggvgd6i2jx] {
    flex: 0 0 auto;
}

.device-key-reveal[b-ggvgd6i2jx] {
    border: 1px solid var(--success-bright);
    background: var(--success-tint);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    margin: var(--space-3) 0;
}

.device-raw-key[b-ggvgd6i2jx] {
    display: block;
    font-family: var(--font-mono, monospace);
    font-size: var(--fs-body);
    word-break: break-all;
    user-select: all;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-sm);
    padding: var(--space-2);
    margin: var(--space-2) 0;
}

.device-reveal-actions[b-ggvgd6i2jx] {
    display: flex;
    gap: var(--space-1);
}

/* Excel-like Device Table */
/* The table itself is the shared `.data-table` (app.css); this carries only the
   page chrome. A full private copy used to live here and beat the global rule on
   specificity — see `shared-component-layer-loads-first`. */
.device-key-table[b-ggvgd6i2jx] {
    margin-top: var(--space-2);
}

.device-key-table th[b-ggvgd6i2jx],
.device-key-table td[b-ggvgd6i2jx] {
    text-align: left;
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border-strong);
    font-size: var(--fs-body);
}

.device-key-table thead th[b-ggvgd6i2jx] {
    position: sticky;
    top: 0;
    background: var(--bg-sunken);
    font-size: var(--fs-caption);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-strong);
    font-weight: var(--weight-bold);
}

.device-key-table tbody tr:hover[b-ggvgd6i2jx] {
    background: var(--slate-50);
}

.device-key-table tr.revoked[b-ggvgd6i2jx] {
    color: var(--text-muted);
    opacity: 0.6;
    background: var(--bg-sunken);
}

/* ---- USB caller-ID box (WebSerial) ----
   Sits above the device-key table: two ways to feed calls to the till, the
   direct-USB one first because it's the common case. The port itself is owned by
   the layout's CallerIdHost, so it outlives this page. */
.callerid-usb[b-ggvgd6i2jx] {
    margin-bottom: var(--space-3);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--border-hair);
}

.callerid-usb-status[b-ggvgd6i2jx] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin: 0 0 var(--space-2);
    color: var(--success-ink);
    font-weight: var(--weight-semibold);
}

.callerid-usb-status[b-ggvgd6i2jx]  svg,
.callerid-usb-connect[b-ggvgd6i2jx]  svg {
    width: 16px;
    height: 16px;
}
/* /Components/Pages/Client/Reports.razor.rz.scp.css */
.as-of[b-t3nt0hynb8] {
    margin: 0 0 1rem;
    font-size: 0.85rem;
}

/* Preset quick-range chips (Today / Yesterday / This week / …). */
.report-presets[b-t3nt0hynb8] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
}

.report-preset[b-t3nt0hynb8] {
    display: inline-flex;
    align-items: center;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface-1);
    color: var(--text-default);
    text-decoration: none;
    font-size: var(--fs-body);
    line-height: 1;
    white-space: nowrap;
}

.report-preset:hover[b-t3nt0hynb8] {
    background: var(--surface-2);
}

.report-preset.active[b-t3nt0hynb8] {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
    font-weight: var(--weight-bold);
}

.report-h2[b-t3nt0hynb8] {
    margin-top: 2rem;
}

.report-table .num[b-t3nt0hynb8] {
    text-align: right;
    white-space: nowrap;
}

.report-table th.num[b-t3nt0hynb8] {
    text-align: right;
}

.small[b-t3nt0hynb8] {
    font-size: 0.8rem;
    margin-top: 0.5rem;
}
/* /Components/Pages/Client/ScheduledOrders.razor.rz.scp.css */
.day-group[b-v8np71uknw] {
    margin-bottom: 1.75rem;
}

.day-group h2[b-v8np71uknw] {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    font-size: 1.05rem;
    margin: 0 0 0.6rem;
}

.day-count[b-v8np71uknw] {
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--text-muted);
}
/* /Components/Pages/Client/SettingsHub.razor.rz.scp.css */
.settings-intro[b-oz6p75s1sv] {
    max-width: 800px;
    margin-bottom: var(--space-4);
    color: var(--text-muted);
}

.hub-grid[b-oz6p75s1sv] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--space-3);
    max-width: 920px;
}

.hub-card[b-oz6p75s1sv] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4);
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-1);
    text-decoration: none;
    color: var(--text-default);
    transition: border-color 0.12s ease;
}

.hub-card:hover[b-oz6p75s1sv] {
    border-color: var(--accent);
}

.hub-icon[b-oz6p75s1sv] { font-size: 1.5rem; }
.hub-title[b-oz6p75s1sv] { font-weight: 600; color: var(--text-strong); }
.hub-desc[b-oz6p75s1sv] { font-size: 0.85rem; color: var(--text-muted); }
/* /Components/Pages/Client/Staff.razor.rz.scp.css */


.staff-panel[b-q8ihsxte6o] {
    /* --surface-1, not the --slate-0 alias: slate-0 is not re-pointed on the
       dark skin, so this panel rendered white under light ink. (Batch E1.) */
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.6rem;
    padding: 1.25rem 1.5rem;
    margin-bottom: 1rem;
}

.staff-panel h2[b-q8ihsxte6o] {
    margin: 0 0 0.5rem;
    font-size: 1.1rem;
}

.invite-row[b-q8ihsxte6o] {
    display: grid;
    grid-template-columns: 2fr 1fr auto;
    gap: 0.75rem;
    align-items: end;
}

/* Width only. This used to restate `.input`'s box — padding, border, radius,
   font — but not its `background`/`color`, so the two <select>s here fell back
   to the UA's grey fill while the text inputs beside them looked fine. A
   <select> is the control that exposes this: its default background is grey
   where a text input's is white. Third instance of the same shape after the
   filter bar and the platform forms; the box belongs to `.input`. */
.invite-input[b-q8ihsxte6o],
.invite-select[b-q8ihsxte6o] {
    width: 100%;
}

.invite-token .token-box[b-q8ihsxte6o] {
    background: var(--bg-sunken);
    border: 1px solid var(--border-strong);
    border-radius: 0.4rem;
    padding: 0.6rem 0.75rem;
    margin: 0.5rem 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-size: 0.85rem;
    word-break: break-all;
    white-space: pre-wrap;
}
/* /Components/Pages/Client/ThermalReceipt.razor.rz.scp.css */
/* Thermal receipt: scoped styles for the 80 mm print layout.
   The surrounding #thermal-receipt div is display:none in normal view;
   app.css @media print rules flip it visible and set @page size. */

.thermal-body[b-lcujct7wb6] {
    font-family: monospace;
    font-size: 9pt;
    line-height: 1.35;
    width: 74mm; /* 80 mm paper - 2 × 3 mm margin set in app.css @page */
    color: var(--slate-950);
}

.t-header[b-lcujct7wb6] {
    text-align: center;
    margin-bottom: 0.4em;
}

.t-header > *[b-lcujct7wb6] {
    display: block;
}

.t-meta[b-lcujct7wb6] {
    margin-bottom: 0.4em;
}

.t-meta > *[b-lcujct7wb6] {
    display: block;
}

.t-rule[b-lcujct7wb6] {
    border: none;
    border-top: 1px dashed var(--slate-950);
    margin: 0.35em 0;
}

.t-line[b-lcujct7wb6] {
    display: flex;
    justify-content: space-between;
    margin-bottom: 0.1em;
}

/* Why this line is not full price — FREE, ½ price, Disc %. It was rendered with
   no rule at all, so it printed at body size hard against the left margin and
   read as another item rather than a note about the one above. Bold because it
   is the line the customer queries at the counter. */
.t-adjust[b-lcujct7wb6] {
    padding-left: 1.2em;
    font-size: 8pt;
    font-weight: 700;
}

.t-mod[b-lcujct7wb6] {
    padding-left: 1.2em;
    font-size: 8pt;
}

.t-instr[b-lcujct7wb6] {
    padding-left: 1.2em;
    font-style: italic;
    font-size: 8pt;
    margin-bottom: 0.2em;
}

.t-totals[b-lcujct7wb6] {
    margin-top: 0.2em;
}

.t-row[b-lcujct7wb6] {
    display: flex;
    justify-content: space-between;
}

.t-total[b-lcujct7wb6] {
    margin-top: 0.2em;
    font-size: 10pt;
}

.t-footer[b-lcujct7wb6] {
    text-align: center;
    margin-top: 0.6em;
    font-size: 8pt;
}

/* The promise header — HUGE, the kitchen spikes the ticket by it. */
.t-slot[b-lcujct7wb6] {
    text-align: center;
    font-size: 2em;
    font-weight: 700;
    letter-spacing: 0.05em;
    margin-bottom: 2mm;
}

/* The debt banner — the paper in hand carries what's still owed. */
.t-unpaid[b-lcujct7wb6] {
    text-align: center;
    font-size: 1.4em;
    font-weight: 700;
    border-top: 1px dashed currentColor;
    border-bottom: 1px dashed currentColor;
    padding: 1mm 0;
    margin: 2mm 0;
}
/* /Components/Pages/Dashboard.razor.rz.scp.css */
.dash-header[b-vf0wsei5p8] {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.dash-header h1[b-vf0wsei5p8] {
    margin: 0;
}

.dash-status[b-vf0wsei5p8] {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.live-badge[b-vf0wsei5p8] {
    font-size: 0.8rem;
    font-weight: 600;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
}

.live-badge.live[b-vf0wsei5p8] {
    background: var(--success-tint);
    color: var(--success-ink);
}

.live-badge.connecting[b-vf0wsei5p8] {
    background: var(--warning-tint);
    color: var(--warning-ink);
}

.auto-toggle[b-vf0wsei5p8] {
    background: transparent;
    border: 1px solid var(--border-strong);
    color: var(--text-default);
    font: inherit;
    font-size: 0.8rem;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    cursor: pointer;
}

.auto-toggle:hover[b-vf0wsei5p8] {
    background: var(--bg-sunken);
}

/* The stat grid and tiles are shared: .dash-grid from app.css, .card.stat-tile
plus .stat-label/.stat-value from the design system, and the .ok/.warn value
colours from app.css. Do NOT add scoped copies here — they win on specificity
and silently make this page's tiles a different size from every other
dashboard's. */

.last-updated[b-vf0wsei5p8] {
    margin-top: 1rem;
    font-size: 0.85rem;
}

.muted[b-vf0wsei5p8] {
    color: var(--text-muted);
}
/* /Components/Pages/Platform/AuditLogDetail.razor.rz.scp.css */
/* .breadcrumb comes from the design system. Do NOT add a scoped copy here:
scoped component CSS carries an extra attribute selector, so it outranks the
global rule and silently re-skins the crumb. */

.detail-grid[b-9ntsvy10vr] {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 0.5rem 1rem;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.5rem;
    padding: 1.25rem 1.5rem;
    margin: 0 0 1.5rem;
}

.detail-grid dt[b-9ntsvy10vr] {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    align-self: center;
}

.detail-grid dd[b-9ntsvy10vr] {
    margin: 0;
    color: var(--text-strong);
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.detail-grid code[b-9ntsvy10vr] {
    background: var(--bg-sunken);
    padding: 0.15rem 0.4rem;
    border-radius: 0.25rem;
    font-size: 0.85rem;
}

.muted-link[b-9ntsvy10vr] {
    color: var(--accent-hover);
    font-size: 0.85rem;
    text-decoration: none;
}

.muted-link:hover[b-9ntsvy10vr] {
    text-decoration: underline;
}

.snapshots[b-9ntsvy10vr] {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
}

@media (max-width: 56rem) {
    .snapshots[b-9ntsvy10vr] {
        grid-template-columns: 1fr;
    }
}

.snapshots h2[b-9ntsvy10vr] {
    font-size: 1rem;
    margin: 0 0 0.5rem;
}

/* Deliberate dark "payload viewer" in both themes — so its text must be a
   FIXED light-warm token (--border-hair flips dark and vanishes on it). */
.json[b-9ntsvy10vr] {
    background: var(--slate-900);
    color: var(--slate-300);
    font-family: var(--font-mono);
    padding: 1rem;
    border-radius: 0.5rem;
    overflow-x: auto;
    font-size: 0.82rem;
    line-height: 1.5;
    margin: 0;
    white-space: pre-wrap;
    word-break: break-word;
}

.muted[b-9ntsvy10vr] {
    color: var(--text-muted);
}
/* /Components/Pages/Platform/AuditLogs.razor.rz.scp.css */
/* Page-specific only — filters / table / pagination are in the shared
   list-page system in wwwroot/app.css. */
.scope-note[b-4s1t23ydha] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    background: var(--info-tint);
    /* --info-500, not --info-tint: a border in the same colour as the fill is
       invisible, so the note had no edge at all. */
    border: 1px solid var(--info-500);
    color: var(--info-ink);
    padding: 0.5rem 0.85rem;
    border-radius: 0.5rem;
    margin-bottom: 1rem;
    font-size: 0.9rem;
}

.scope-note code[b-4s1t23ydha] {
    background: var(--info-tint);
    padding: 0.1rem 0.4rem;
    border-radius: 0.25rem;
}

.scope-note a[b-4s1t23ydha] {
    margin-left: auto;
    color: var(--accent-hover);
}
/* /Components/Pages/Platform/TenantDetail.razor.rz.scp.css */
/* .breadcrumb comes from the design system. Do NOT add a scoped copy here:
scoped component CSS carries an extra attribute selector, so it outranks the
global rule and silently re-skins the crumb. */

.detail-header[b-w39whbh1dh] {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    margin-bottom: 1.25rem;
}

.detail-header h1[b-w39whbh1dh] {
    margin: 0;
}

.detail-actions[b-w39whbh1dh] {
    margin-left: auto;
    display: flex;
    gap: 0.5rem;
}

.detail-grid[b-w39whbh1dh] {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 0.5rem 1rem;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.5rem;
    padding: 1.25rem 1.5rem;
    margin: 0 0 1.5rem;
}

.detail-grid dt[b-w39whbh1dh] {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    align-self: center;
}

.detail-grid dd[b-w39whbh1dh] {
    margin: 0;
    color: var(--text-strong);
}

.detail-grid code[b-w39whbh1dh] {
    background: var(--bg-sunken);
    padding: 0.15rem 0.4rem;
    border-radius: 0.25rem;
    font-size: 0.85rem;
}

.swatches[b-w39whbh1dh] {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.swatch[b-w39whbh1dh] {
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 0.25rem;
    border: 1px solid var(--border-strong);
    display: inline-block;
}

.action-panel[b-w39whbh1dh] {
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.5rem;
    padding: 1.25rem 1.5rem;
}

.action-panel h2[b-w39whbh1dh] {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
}

/* Rhythm and measure only. This rule used to re-declare the flex column, the
   label type and the input box as well — the very thing the note at the top of
   this file warns about — and being scoped it outranked them all. The visible
   cost was `.checkbox-field`: the shared rule turns a field into a row, and
   `.action-panel .field` (0,2,0, plus the isolation attribute) put it back into
   a column, leaving every checkbox stranded above its own label. */
.action-panel .field[b-w39whbh1dh] {
    margin: 0.75rem 0 1rem;
    max-width: 28rem;
}

.domains-table[b-w39whbh1dh] {
    width: 100%;
    border-collapse: collapse;
    margin: 0.75rem 0 1.25rem;
}

.domains-table th[b-w39whbh1dh] {
    text-align: left;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--border-hair);
}

.domains-table td[b-w39whbh1dh] {
    padding: 0.55rem 0.6rem;
    border-bottom: 1px solid var(--border-hair);
    vertical-align: middle;
}

.domain-actions[b-w39whbh1dh] {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    flex-wrap: wrap;
}
/* /Components/Pages/Platform/Tenants.razor.rz.scp.css */
/* Page-specific only — filters / table / pagination / status pills are in
   the shared list-page system in wwwroot/app.css. */
/* Wider filter controls than the global default (free-text + status select). */
.filter-field input[b-mbiy3a76zr],
.filter-field select[b-mbiy3a76zr] {
    min-width: 12rem;
}
/* /Components/Pages/Platform/WebhookLogDetail.razor.rz.scp.css */
/* .breadcrumb comes from the design system. Do NOT add a scoped copy here:
scoped component CSS carries an extra attribute selector, so it outranks the
global rule and silently re-skins the crumb. */

.detail-grid[b-e21nda9xe9] {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 0.5rem 1rem;
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: 0.5rem;
    padding: 1.25rem 1.5rem;
    margin: 0 0 1.5rem;
}

.detail-grid dt[b-e21nda9xe9] {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    align-self: center;
}

.detail-grid dd[b-e21nda9xe9] {
    margin: 0;
    color: var(--text-strong);
}

.detail-grid code[b-e21nda9xe9] {
    background: var(--bg-sunken);
    padding: 0.15rem 0.4rem;
    border-radius: 0.25rem;
    font-size: 0.85rem;
}

.payload h2[b-e21nda9xe9] {
    font-size: 1rem;
    margin: 0 0 0.5rem;
}

/* Deliberate dark "payload viewer" in both themes — so its text must be a
   FIXED light-warm token (--border-hair flips dark and vanishes on it). */
.json[b-e21nda9xe9] {
    background: var(--slate-900);
    color: var(--slate-300);
    font-family: var(--font-mono);
    padding: 1rem;
    border-radius: 0.5rem;
    overflow-x: auto;
    font-size: 0.82rem;
    line-height: 1.5;
    margin: 0;
    white-space: pre-wrap;
    word-break: break-word;
}

.muted[b-e21nda9xe9] {
    color: var(--text-muted);
}
/* /Components/Pages/Platform/WebhookLogs.razor.rz.scp.css */
/* Page-specific only — filters / table / pagination / provider pill are in
   the shared list-page system in wwwroot/app.css. */
.event-id[b-0ypfmsy8vc] {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    color: var(--text-muted);
}

/* Slightly narrower than the tenants filter (provider select + two dates). */
.filter-field input[b-0ypfmsy8vc],
.filter-field select[b-0ypfmsy8vc] {
    min-width: 10rem;
}
/* /Components/Shared/CallerPopupHost.razor.rz.scp.css */
/* Inbound caller-ID pop-up cards. Moved here from Pos.razor.css when the cards
   were hoisted out of the till and into the layout: scoped CSS is rewritten per
   component (.x[b-parentId]), so rules left behind in the page's stylesheet stop
   matching the host's markup entirely (§8 blazor-extraction-css-and-types).

   Float the toast as a card near the order panel (Batch R5, matching the till
   mockup) rather than a full-width banner that shoves the work area down. Fixed
   to the top-right so it never disturbs layout. */
.incoming-calls[b-j78osifu9h] {
    position: fixed;
    top: calc(var(--space-2) + 60px);
    right: var(--space-3);
    z-index: 30;
    width: 360px;
    max-width: calc(100vw - 2 * var(--space-3));
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

/* Incoming-call toast — the till mockup's "service bell" card. */
/* Identity on the first row, the decision on the second — NOT one flex row.
   The card is 360px; icon (40) + "Start order" + "Dismiss" (~200) + gaps left the
   body about 90px, and `.ic-body` carries `min-width: 0`, which PERMITS that
   crush rather than preventing it. A caller called "Sam-pick -paylink" then broke
   at its hyphens into three lines and "10 past orders · last: 1 EMBERTON PARK,
   HU7 3EP" wrapped over six — on the one surface whose whole job is to tell the
   operator who is on the phone before they answer. Giving the actions their own
   row hands the text the full width. */
.incoming-call-toast[b-j78osifu9h] {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
        "icon    body"
        "actions actions";
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2);
    border: 1px solid var(--border-hair);
    border-left: 4px solid var(--brand);
    border-radius: var(--radius-md);
    background: var(--surface-1);
    box-shadow: var(--elev-3);
    animation: ic-call-in-b-j78osifu9h 0.28s var(--ease);
}

@keyframes ic-call-in-b-j78osifu9h {
    from { opacity: 0; transform: translateY(-8px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}

.incoming-call-toast.known[b-j78osifu9h] {
    border-left-color: var(--status-success);
}

.incoming-call-toast.unknown[b-j78osifu9h] {
    border-left-color: var(--text-faint);
}

/* Circular bell badge that rings gently to draw the eye. */
.incoming-call-toast .ic-icon[b-j78osifu9h] {
    grid-area: icon;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-pill);
    background: var(--brand-soft);
    color: var(--brand-ink);
}

.incoming-call-toast.known .ic-icon[b-j78osifu9h] {
    background: var(--grass-tint);
    color: var(--grass-ink);
}

.incoming-call-toast.unknown .ic-icon[b-j78osifu9h] {
    background: var(--bg-sunken);
    color: var(--text-muted);
}

/* ::deep — the SVG is rendered by the Icon child component, whose scope differs
   from this stylesheet's (scoped CSS doesn't cross component boundaries). */
.incoming-call-toast .ic-icon[b-j78osifu9h]  svg {
    width: 20px;
    height: 20px;
    transform-origin: 50% 20%;
    animation: ic-ring-b-j78osifu9h 1.6s ease-in-out infinite;
}

@keyframes ic-ring-b-j78osifu9h {
    0%, 60%, 100% { transform: rotate(0); }
    70% { transform: rotate(12deg); }
    80% { transform: rotate(-10deg); }
    90% { transform: rotate(6deg); }
}

@media (prefers-reduced-motion: reduce) {
    .incoming-call-toast[b-j78osifu9h] { animation: none; }
    .incoming-call-toast .ic-icon[b-j78osifu9h]  svg { animation: none; }
}

.incoming-call-toast .ic-body[b-j78osifu9h] {
    grid-area: body;
    min-width: 0;
}

.incoming-call-toast .ic-line1[b-j78osifu9h] {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    flex-wrap: wrap;
    font-size: var(--fs-h3);
    font-weight: var(--weight-bold);
    color: var(--text-strong);
}

.incoming-call-toast .ic-number[b-j78osifu9h] {
    font-family: var(--font-mono);
    font-feature-settings: "tnum" 1;
    font-weight: var(--weight-semibold);
    color: var(--text-default);
}

.incoming-call-toast .ic-actions[b-j78osifu9h] {
    grid-area: actions;
    display: flex;
    justify-content: flex-end;
    gap: var(--space-1);
}

/* The operator taps these mid-call, on a touchscreen — they measured 41px, the
   same sub-floor miss as .recall-lookup. */
.incoming-call-toast .ic-actions button[b-j78osifu9h] {
    min-height: 44px;
}

/* /Components/Shared/ConfirmHost.razor.rz.scp.css */
/* Stacking order, highest last — a confirmation MUST out-rank whatever raised
   it, or the operator sees two buttons and no question:
     toasts 300 · doorbell 1000 · confirm 1050 · reconnect modal 1100
   At 900 this sat BELOW the doorbell's 1000 interrupt overlay, so Reject on a
   doorbell card opened a dialog buried behind the card — the message hidden,
   only the buttons showing. bUnit renders no CSS and cannot see this; it was
   found in a browser pass. The reconnect modal stays on top of everything,
   because a dropped circuit makes every dialog below it dead anyway. */
.confirm-backdrop[b-h66hsw2n3n] {
    position: fixed;
    inset: 0;
    background: rgba(15, 18, 30, 0.45);
    z-index: 1050;
}

.confirm-dialog[b-h66hsw2n3n] {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1051;
    width: min(28rem, calc(100vw - 2rem));
    background: var(--surface-1);
    color: var(--text-strong);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-3);
    padding: var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.confirm-title[b-h66hsw2n3n] {
    margin: 0;
    font-size: var(--fs-h3);
    font-weight: var(--weight-semibold);
}

.confirm-message[b-h66hsw2n3n] {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--fs-body);
}

.confirm-actions[b-h66hsw2n3n] {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-1);
}
/* /Components/Shared/IncomingOrderDoorbell.razor.rz.scp.css */
/* Fixed interrupt overlay — sits above the sidebar rail (.topbar, z-index:100)
   and centred near the top so it reads as a doorbell, clear of the toast corner. */
.doorbell-overlay[b-2o3sxupo69] {
    position: fixed;
    top: 4.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    width: min(92vw, 380px);
}

.doorbell-card[b-2o3sxupo69] {
    background: var(--surface-1);
    border: 1px solid var(--accent);
    border-left: 4px solid var(--accent);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-2, 0 10px 25px rgba(0, 0, 0, 0.18));
    padding: var(--space-3);
    animation: doorbell-in-b-2o3sxupo69 0.18s ease-out;
}

@keyframes doorbell-in-b-2o3sxupo69 {
    from { transform: translateY(-8px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

/* ---- Visual escalation (order-intake epic, decision 5) ----
   An unanswered order gets harder to ignore over time. This is the channel that
   ALWAYS works: browsers refuse to play audio until the operator has touched the
   page, so on a till nobody has clicked since load the chime is silent and this
   is the only thing telling the kitchen an order is waiting. Hence "visual
   escalation is not optional" — it is the fallback for a platform rule, not a
   flourish on top of the sound.

   Steps are deliberately restrained: this has to remain survivable for a whole
   Friday service, so it grows in weight and motion rather than becoming a strobe,
   and it stops escalating at 3. */
.doorbell-overlay[data-urgency="1"] .doorbell-card[b-2o3sxupo69] {
    border-color: var(--status-warn);
    border-left-width: 6px;
}

.doorbell-overlay[data-urgency="2"] .doorbell-card[b-2o3sxupo69],
.doorbell-overlay[data-urgency="3"] .doorbell-card[b-2o3sxupo69] {
    border-color: var(--status-urgent);
    border-left-width: 8px;
    box-shadow: var(--elev-3, 0 14px 34px rgba(0, 0, 0, 0.26));
    animation: doorbell-nag-b-2o3sxupo69 1.6s ease-in-out infinite;
}

.doorbell-overlay[data-urgency="3"] .doorbell-card[b-2o3sxupo69] {
    animation-duration: 1s;
}

@keyframes doorbell-nag-b-2o3sxupo69 {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-3px); }
}

/* Respect the OS setting: motion is one signal, and the border weight and colour
   still carry the escalation without it. */
@media (prefers-reduced-motion: reduce) {
    .doorbell-overlay[data-urgency] .doorbell-card[b-2o3sxupo69] {
        animation: none;
    }
}

/* Shown only when the chime reported that no sound came out. The operator cannot
   be told this BY sound, by definition. */
.doorbell-muted-warning[b-2o3sxupo69] {
    margin: 0 0 0.4rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--status-urgent);
}

.doorbell-head[b-2o3sxupo69] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.35rem;
}

.doorbell-bell[b-2o3sxupo69] { font-size: 1.15rem; }
.doorbell-title[b-2o3sxupo69] { font-weight: 700; color: var(--text-strong); }

/* Amber, not red: the order is safe — cooked and paid — and what is
   wrong is only that nobody has looked at it. Alarm here would compete with the
   genuinely-unanswered cards beside it. */
.doorbell-auto-note[b-2o3sxupo69] {
    margin: 0 0 var(--space-1);
    font-size: var(--fs-caption);
    color: var(--warning-ink);
}

.doorbell-num[b-2o3sxupo69] {
    margin-left: auto;
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

.doorbell-meta[b-2o3sxupo69] {
    margin: 0 0 0.5rem;
    color: var(--text-default);
    font-size: 0.9rem;
}

.doorbell-lines[b-2o3sxupo69] {
    margin: 0 0 0.75rem;
    padding-left: 1.1rem;
    color: var(--text-muted);
    font-size: 0.85rem;
    max-height: 6rem;
    overflow-y: auto;
}

.doorbell-actions[b-2o3sxupo69] {
    display: flex;
    gap: 0.5rem;
}

.doorbell-actions .btn[b-2o3sxupo69] {
    flex: 1;
}
/* /Components/Shared/PosBottomDock.razor.rz.scp.css */
/* Fixed bottom dock. Collapsed = a thin always-present bar; expands
   upward into the rails. pointer-events pass through everywhere except the bar
   and the open body, so the collapsed dock never blocks the POS behind it. */
.pos-dock[b-zs47swwf8g] {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Low priority: above page content but BELOW every modal/drawer (backdrop
       z-10, line-drawer z-11, checkout/doorbell z-1000) so an open drawer's
       bottom buttons aren't intercepted by the collapsed bar. */
    z-index: 5;
    display: flex;
    flex-direction: column;
    pointer-events: none;
}

.dock-bar[b-zs47swwf8g],
.dock-body[b-zs47swwf8g] {
    pointer-events: auto;
}

/* Collapsed bar: a thick strip split down the middle — Collection (left),
   Delivery (right), the expand caret on the centre separator.

   The design system's `.dock-bar` now supplies the grid, the width, the height
   (`var(--dock-height)`), the button reset and the cursor. What stays here is
   only what the shared layer does NOT express, because the till's dock is a
   click-through overlay rather than an opaque panel:
     · `order` — the dock expands UPWARD, so the body must lay out before the
       bar in the container's flex column. Without it the body renders below the
       bar, i.e. off the bottom of the screen.
     · `align-items: stretch` — the DS centres; the halves fill the bar's full
       height so their padding defines the hit area.
     · `padding: 0` — the DS pads the bar; here the padding lives on .dock-half
       so the centre separator runs edge to edge.
     · the border-top and --surface-2 fill — the DS bar is transparent, which
       works on its opaque `.dock` container but not on our transparent one. */
.dock-bar[b-zs47swwf8g] {
    order: 2;
    align-items: stretch;
    padding: 0;
    border-top: 1px solid var(--border-strong);
    background: var(--surface-2);
    color: var(--text-strong);
    font: inherit;
    text-align: left;
}

/* The DS .dock-half has no padding — it assumes the bar pads instead. */
.dock-half[b-zs47swwf8g] { padding: 0 var(--space-4); }

/* .dock-count, .dock-label and .dock-caret are inherited from the design system
   verbatim — the till was already declaring them identically, which is what made
   this family safe to converge. The one exception is the label's tracking: the
   DS uses --tracking-eyebrow where the till has always used 0.14em. */
.dock-label .t[b-zs47swwf8g] { letter-spacing: 0.14em; }
.dock-label b[b-zs47swwf8g] { font-weight: 700; color: var(--text-strong); }

.dock-sep[b-zs47swwf8g] {
    display: grid;
    place-items: center;
    padding: 0 var(--space-2);
    border-left: 1px solid var(--border-strong);
    border-right: 1px solid var(--border-strong);
}
.dock-caret[b-zs47swwf8g] { font-size: 1rem; color: var(--text-muted); }

.dock-cash-pill[b-zs47swwf8g] {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-weight: 700;
    font-size: var(--fs-caption);
    color: var(--warning-ink);
    background: var(--warning-tint);
    padding: 3px 10px;
    border-radius: var(--radius-pill);
}

/* Bar-level awaiting-payment counter — same shape as the cash pill,
   amber, so an unpaid link order is visible even with the dock collapsed. */
.dock-awaiting-pill[b-zs47swwf8g] {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-weight: 700;
    font-size: var(--fs-caption);
    color: var(--warning-ink);
    background: var(--warning-tint);
    padding: 3px 10px;
    border-radius: var(--radius-pill);
}

.pulse[b-zs47swwf8g] { animation: dock-pulse-b-zs47swwf8g 1.2s ease-in-out infinite; }
@keyframes dock-pulse-b-zs47swwf8g {
    50% { opacity: 0.45; }
}

/* The DS supplies the two-column grid, max-height and scrolling. The delta:
   `order` (expand upward — see .dock-bar), and the fact that this body is its
   own floating surface rather than a region inside an opaque `.dock`, so it
   carries the fill and the lift. `gap`/`padding` are zeroed because the rails
   pad themselves — that is what lets the centre divider run the full height. */
.dock-body[b-zs47swwf8g] {
    order: 1;
    gap: 0;
    padding: 0;
    background: var(--surface-1);
    box-shadow: 0 -8px 20px rgba(0, 0, 0, 0.12);
}

/* Padding lives on the rails so the centre divider runs the full height.
   Do NOT set `display` here. `.dock-rail` was already the shared name before
   this batch, so the design system's flex column and its `gap: var(--space-1)`
   are ALREADY what spaces the cards — overriding it to `block` silently drops
   that gap and shortens the open body by 16px. (Measured; the geometry probe
   caught it.) */
.dock-rail[b-zs47swwf8g] { padding: var(--space-3) var(--space-4); min-width: 0; }
.dock-rail:first-child[b-zs47swwf8g] { border-right: 1px solid var(--border-hair); }

.dock-rail h3[b-zs47swwf8g] {
    margin: 0 0 var(--space-2);
    font-size: var(--fs-h3);
    color: var(--text-strong);
}

.dock-card[b-zs47swwf8g] {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: var(--space-2) var(--space-2) var(--space-2) calc(var(--space-2) + 6px);
    margin-bottom: var(--space-2);
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-1);
}

/* The heat edge (DS-B) — the rail carries AGE, banded by the tenant's
   warn/urgent thresholds. One meaning per channel: the edge never encodes
   lifecycle status (that's the pill in the card head). */
.dock-card:not(.run-card)[b-zs47swwf8g]::before {
    content: "";
    position: absolute;
    left: 0; top: 8px; bottom: 8px;
    width: var(--heat-edge-width);
    border-radius: 0 3px 3px 0;
    background: var(--ticket-edge, var(--status-fresh));
}
.dock-card.ticket-fresh[b-zs47swwf8g]  { --ticket-edge: var(--status-fresh); }
.dock-card.ticket-warn[b-zs47swwf8g]   { --ticket-edge: var(--status-warn); }
.dock-card.ticket-urgent[b-zs47swwf8g] { --ticket-edge: var(--status-urgent); }
/* Urgent earns one extra affordance beyond colour — a heavier rail. */
.dock-card.ticket-urgent[b-zs47swwf8g] { --heat-edge-width: 6px; }

/* The age numeral takes the edge's colour, so minutes and rail read as one signal. */
.dock-age[b-zs47swwf8g] {
    margin-left: 0.25rem;
    font-family: var(--font-mono);
    font-feature-settings: "tnum" 1, "zero" 1;
    font-weight: var(--weight-bold);
    font-size: 0.8rem;
    color: var(--ticket-edge, var(--text-muted));
}

/* Status pill — categorical lifecycle state, mirrors design-system
   components.css .pill (glyphs keep the state legible in greyscale). */
.pill[b-zs47swwf8g] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-05);
    padding: var(--space-05) var(--space-1);
    border-radius: var(--radius-pill);
    font-size: var(--fs-caption);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
}
.pill-success[b-zs47swwf8g] { background: var(--grass-tint); color: var(--grass-ink); }
.pill-info[b-zs47swwf8g]    { background: var(--brand-soft); color: var(--brand-ink); }
.pill-neutral[b-zs47swwf8g] { background: var(--bg-sunken);  color: var(--text-default); }
.pill[b-zs47swwf8g]::before { font-weight: var(--weight-bold); }
.pill-success[b-zs47swwf8g]::before { content: "\2713"; }  /* ✓ */
.pill-neutral[b-zs47swwf8g]::before { content: "\00B7"; }  /* · */

.dock-card-head[b-zs47swwf8g] {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.dock-status[b-zs47swwf8g] { color: var(--text-muted); font-size: 0.8rem; }

.pay-badge[b-zs47swwf8g] {
    margin-left: auto;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
}

.pay-badge.paid[b-zs47swwf8g] { background: var(--success-tint); color: var(--success-ink); }
.pay-badge.cash[b-zs47swwf8g] { background: var(--warning-tint); color: var(--warning-ink); }
.pay-badge.awaiting[b-zs47swwf8g] { background: var(--warning-tint); color: var(--warning-ink); }
/* A dead link is a different state from a ticking one, so it must not read
   the same. Critical rather than warning — nothing is in flight any more, and
   the operator has to act (resend / switch / cancel) rather than wait. */
.pay-badge.awaiting.expired[b-zs47swwf8g] { background: var(--critical-tint); color: var(--critical-ink); }
/* Refunded is a CLOSED money state, not an outstanding one — so it must
   not borrow amber (which on this rail means "someone still owes something").
   Neutral says "settled, nothing to chase" while still being visibly not-PAID. */
.pay-badge.refunded[b-zs47swwf8g] { background: var(--bg-sunken); color: var(--text-muted); }

/* Awaiting-payment cards are AMBER from placement (an obligation the
   kitchen isn't cooking yet); the heat edge carries the same signal instead of
   the age band. After ~15 min unpaid they escalate — tinted card + pulsing
   badge — but never auto-cancel: the operator decides (resend/switch/cancel). */
.dock-card.awaiting-pay[b-zs47swwf8g] { --ticket-edge: var(--status-warn); }
.dock-card.awaiting-late[b-zs47swwf8g] {
    --heat-edge-width: 6px;
    background: var(--warning-tint);
}
.dock-card.awaiting-late .pay-badge.awaiting[b-zs47swwf8g] {
    animation: dock-pulse-b-zs47swwf8g 1.2s ease-in-out infinite;
}

.dock-cancel[b-zs47swwf8g] { color: var(--heat-urgent-ink); }

/* 10c: money out. Same urgent ink as Cancel — both send something away that
   cannot be taken back — but deliberately NOT a filled button: it sits beside
   Reprint on a card whose primary action is the kitchen's, and a solid red
   button there is a mis-tap waiting to happen. The confirm is the real guard. */
.dock-refund[b-zs47swwf8g] {
    flex: 0 0 auto;
    padding: 0 var(--space-1);
    color: var(--heat-urgent-ink);
    font-size: var(--fs-caption);
    white-space: nowrap;
}

/* Flex + gap, not bare text: Razor strips the whitespace around a block-level
   `@if`, so the delivery card's name / phone / item-count rendered with NOTHING
   between them ("Ada Lovelace+4477009005553 items") — the line the counter reads
   out to a driver. The collection card only escaped because it carries a literal
   `·`. The gap supplies the separation at both boundaries and for whatever a
   later card adds; don't go back to relying on source whitespace. */
.dock-card-meta[b-zs47swwf8g] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1);
    color: var(--text-muted);
    font-size: 0.85rem;
}
.dock-hint[b-zs47swwf8g] { font-size: 0.8rem; }
.dock-collect[b-zs47swwf8g] { align-self: flex-start; }

.dock-actions[b-zs47swwf8g] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.dock-convert[b-zs47swwf8g] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-top: var(--space-1);
}

.dock-convert .form-control[b-zs47swwf8g] { flex: 1 1 8rem; min-height: 36px; }

/* ---- Delivery rail ---- */
.dock-shift-strip[b-zs47swwf8g] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
}

.shift-chip[b-zs47swwf8g] {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.8rem;
    background: var(--bg-sunken);
    border: 1px solid var(--border-hair);
}

.shift-chip.on[b-zs47swwf8g] {
    background: var(--success-tint);
    color: var(--success-ink);
    border-color: transparent;
}

.dock-send[b-zs47swwf8g] {
    align-self: stretch;
    min-height: 40px;
}

/* Building is neither good news nor bad — neutral, not brand (DS-B). */
.run-card.building[b-zs47swwf8g] { border-left: 3px solid var(--border-strong); }
.run-card.dispatched[b-zs47swwf8g] { border-left: 3px solid var(--status-success); }

.run-drop[b-zs47swwf8g] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    font-size: 0.85rem;
    padding: 2px 0;
}

.run-actions[b-zs47swwf8g] {
    display: flex;
    gap: var(--space-1);
    margin-top: var(--space-1);
}

/* ---- Promise-aware dock additions ---- */

/* Item-count expander on a card ("4 items ▸"). */
.dock-items[b-zs47swwf8g] {
    margin-left: auto;
    padding: 0;
    font-size: var(--fs-caption);
    white-space: nowrap;
}
.dock-item-list[b-zs47swwf8g] {
    margin: var(--space-05) 0 0;
    padding-left: var(--space-2);
    font-size: var(--fs-caption);
    color: var(--text-muted);
}

/* Delivery confirm-back line: street + BOLD postcode (what drivers key on). */
.dock-card-address[b-zs47swwf8g] {
    display: flex;
    align-items: baseline;
    gap: var(--space-1);
    min-width: 0;
}
.dock-card-address .dock-street[b-zs47swwf8g] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.dock-card-address .dock-postcode[b-zs47swwf8g] {
    margin-left: auto;
    font-weight: var(--weight-semibold);
    white-space: nowrap;
}
.dock-phone[b-zs47swwf8g] { color: var(--text-muted); }

/* Later-today strip: collapsed one-liner + compact rows when revealed. */
.dock-later-toggle[b-zs47swwf8g] {
    width: 100%;
    text-align: left;
    padding: var(--space-05) 0;
}
.dock-later-row[b-zs47swwf8g] {
    display: flex;
    align-items: baseline;
    gap: var(--space-1);
    padding: var(--space-05) var(--space-1);
    color: var(--text-muted);
    font-size: var(--fs-caption);
}
.dock-later-row .dock-age[b-zs47swwf8g] { margin-left: auto; }

/* ---- Raise/send-back controls and the auto-accepted marker. These carry
   meaning the operator has to read, so they must stay styled — bUnit renders no
   CSS, so markup landing with no rules fails nothing and looks fine. ---- */

/* Locked decision 11. The row is one line and the age already claims
   `margin-left: auto`, so this sits after it rather than fighting for the slot. */
.dock-start-now[b-zs47swwf8g] {
    flex: 0 0 auto;
    padding: 0 var(--space-1);
    font-size: var(--fs-caption);
    white-space: nowrap;
}

/* Why a card is in the live rail early, and why it arrived silently. Muted: an
   explanation, not a state to act on. */
.dock-raised[b-zs47swwf8g] {
    font-size: var(--fs-caption);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--text-muted);
}

.dock-send-back[b-zs47swwf8g] {
    padding: 0 var(--space-1);
    font-size: var(--fs-caption);
    white-space: nowrap;
}

/* Decision 6: in the book, nothing made yet. Muted — it explains the absence of
   buttons rather than asking for anything. */
.dock-awaiting-accept[b-zs47swwf8g] { color: var(--text-muted); }

/* "unanswered for N minutes" escalates on the tenant's OWN kitchen
   thresholds, so late means the same thing here as on every other chit. */
.dock-awaiting-accept.unanswered-warn[b-zs47swwf8g] { color: var(--warning-ink); font-weight: var(--weight-semibold); }
.dock-awaiting-accept.unanswered-urgent[b-zs47swwf8g] { color: var(--critical-ink); font-weight: var(--weight-bold); }

/* Amber, not red: the order is safe — cooked and paid — but UNSEEN, so
   it wants attention rather than alarm. */
.dock-auto-accepted[b-zs47swwf8g] {
    font-size: var(--fs-caption);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    font-weight: var(--weight-bold);
    color: var(--warning-ink);
}

/* ---- "Need more time" ----------------------------------------
   The disclosure and its presets sit in the same wrapped action row as
   Reprint, so they inherit .dock-actions' flex + gap and need no layout of
   their own. Kept visually quiet: this is a considered decision, not a
   primary action, and it must not compete with Collected. */
.dock-extend[b-zs47swwf8g] {
    flex: 0 0 auto;
    padding: 0 var(--space-1);
    font-size: var(--fs-caption);
    white-space: nowrap;
}

.dock-extend-presets[b-zs47swwf8g] {
    display: inline-flex;
    gap: 2px;
}

/* Touch targets, because these are the buttons that actually move a promise —
   a mis-tap here is a wrong time shown to a customer, not a wasted click. */
.dock-extend-preset[b-zs47swwf8g] {
    min-width: 44px;
    min-height: 36px;
    padding: 0 var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--fs-caption);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
}

.dock-extend-preset:hover:not(:disabled)[b-zs47swwf8g] {
    background: var(--warning-tint);
    border-color: var(--warning-ink);
}

/* The badge on a card whose promise has already moved. Amber for the same
   reason busy mode is: the shop is behind, and a due time that slid silently
   is indistinguishable from a kitchen simply running late. */
.dock-extended[b-zs47swwf8g] {
    flex: 0 0 auto;
    align-self: center;
    padding: 0 var(--space-1);
    border-radius: var(--radius-sm);
    background: var(--warning-tint);
    color: var(--warning-ink);
    font-size: var(--fs-caption);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
}

/* The deferred-book count on the COLLAPSED bar: the only thing
   anyone sees on a quiet morning, since the dock starts closed. Neutral and
   NOT pulsing on purpose — it is a briefing, not a summons; the doorbell is
   what demands attention, and a second pulsing pill would dilute it. */
.dock-later-pill[b-zs47swwf8g] {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--bg-sunken);
    color: var(--text-muted);
    font-size: var(--fs-caption);
    white-space: nowrap;
}

/* Day headers inside the strip, shown only once it spans more than one
   trading day. */
.dock-later-day[b-zs47swwf8g] {
    margin: var(--space-1) 0 2px;
    color: var(--text-muted);
    font-size: var(--fs-caption);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
/* /Components/Shared/PosTouchKeyboard.razor.rz.scp.css */
/* POS touch keyboard. Big resistive-touch targets (the till is an
   old single-touch panel); tokens only (arch test forbids raw hex). The JS
   module stamps data-layout: "digits" shows the numpad, "full" the board. */

.pos-kb[b-rwxyc0j3sk] {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    padding: var(--space-1);
    border-top: 1px solid var(--border-strong);
    background: var(--surface-2);
    user-select: none;
    touch-action: manipulation;
}

.kb-row[b-rwxyc0j3sk] {
    display: flex;
    gap: var(--space-05);
}

.kb-key[b-rwxyc0j3sk] {
    flex: 1 1 0;
    min-height: 52px;
    min-width: 0;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    color: var(--text-default);
    font: inherit;
    font-size: var(--fs-base);
    font-weight: var(--weight-medium);
    text-align: center;
    cursor: pointer;
}

.kb-key:active[b-rwxyc0j3sk] {
    background: var(--surface-2);
}

.kb-space[b-rwxyc0j3sk] { flex: 3 1 0; }
.kb-next[b-rwxyc0j3sk] {
    flex: 1.4 1 0;
    font-weight: var(--weight-semibold);
}
.kb-back[b-rwxyc0j3sk] { font-size: var(--fs-h3); }

/* Numpad (phone/prefix fields): a 3-wide grid of oversized keys. */
.kb-numpad[b-rwxyc0j3sk] {
    display: none;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-05);
}
.kb-numpad .kb-key[b-rwxyc0j3sk] { min-height: 60px; }

.pos-kb[data-layout="digits"] .kb-main[b-rwxyc0j3sk] { display: none; }
.pos-kb[data-layout="digits"] .kb-numpad[b-rwxyc0j3sk] { display: grid; }
.pos-kb[data-layout="digits"] .kb-space[b-rwxyc0j3sk] { display: none; }

.kb-main[b-rwxyc0j3sk] {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
}
/* /Components/Shared/ShopPresenceHost.razor.rz.scp.css */
/* Connection-degraded banner. Moved here from Pos.razor.css with the heartbeat:
   scoped CSS is rewritten per component, so a rule left behind in the page's
   stylesheet would stop matching this host's markup entirely
   (§8 blazor-extraction-css-and-types). */
.shop-offline-banner[b-kd79x6wpqp] {
    background: var(--warning-tint);
    border: 1px solid var(--warning-bright);
    color: var(--warning-ink);
    padding: 12px 16px;
    border-radius: 6px;
    margin-bottom: 12px;
    font-size: 14px;
    line-height: 1.4;
}
/* /Components/Shared/ToastHost.razor.rz.scp.css */
.toast-host[b-9unqvvxucb] {
    position: fixed;
    /* Top-right of the content area, above every surface (the sidebar rail and
       the POS dock are z-index 100) so an error is never buried (issue 7). The
       top inset clears the page head, not the old top header. */
    top: 4.25rem;
    right: var(--space-3);
    z-index: 300;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 26rem;
    width: calc(100vw - 2 * var(--space-3));
    pointer-events: none;
}

.toast[b-9unqvvxucb] {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-hair);
    border-left: 4px solid var(--border-strong);
    background: var(--surface-1);
    color: var(--text-strong);
    box-shadow: var(--elev-3);
    font-size: var(--fs-body);
    line-height: 1.4;
    pointer-events: auto;
    animation: toast-in-b-9unqvvxucb 160ms ease-out;
}

@keyframes toast-in-b-9unqvvxucb {
    from { opacity: 0; transform: translateY(-0.4rem) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .toast[b-9unqvvxucb] { animation: none; }
}

.toast-success[b-9unqvvxucb] { border-left-color: var(--success-500); }
.toast-info[b-9unqvvxucb]    { border-left-color: var(--info-500); }
.toast-error[b-9unqvvxucb]   { border-left-color: var(--status-critical); }

.toast-msg[b-9unqvvxucb] {
    flex: 1 1 auto;
    padding-top: 0.15rem;
}

.toast-close[b-9unqvvxucb] {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: var(--radius-md);
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
}
.toast-close:hover[b-9unqvvxucb] {
    color: var(--text-strong);
    background: var(--bg-sunken);
}
.toast-close:focus-visible[b-9unqvvxucb] {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
