/* Admin-console page-scoped styles. app.css sits just under the public-surface size budget, so admin-only
   blocks land here (loaded after app.css, same mobile-first authoring rules as its FB-92 block: base rules
   target 375px, ONLY min-width queries layer wider layouts on, everything contained per FB-86). */

/* FB-91 governance inbox mobile-first — start */
/* The guided governance workspace's unified inbox, authored mobile-first like this file's FB-92 block: base rules
   target 375px — the count chips wrap, each row stacks its summary and detail into one column, and the expanded
   detail's context panel sits ABOVE the decision affordances — and min-width queries lay the context beside the
   decisions once there's width. Everything stays inside the clipped admin canvas (FB-86 containment rule):
   max-width: 100% / min-width: 0 down the chain. Scoped to the governance-inbox-*/governance-term-context
   classes only. Layout only — no decision-flow changes. */
.governance-inbox-chips,
.governance-inbox-list,
.governance-inbox-row,
.governance-inbox-row-heading,
.governance-inbox-detail,
.governance-inbox-detail-grid,
.governance-inbox-decision,
.governance-term-context {
    max-width: 100%;
    min-width: 0;
}

.governance-inbox-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}

.governance-inbox-row {
    padding: 1rem;
    border: 1px solid var(--mud-palette-lines-default, rgba(0, 0, 0, 0.12));
    border-radius: 8px;
}

/* Base = 375px: icon + heading share the first line; the Decide toggle drops to its own full-width 44px row. */
.governance-inbox-row-summary {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.75rem;
    align-items: start;
}

.governance-inbox-row-toggle.mud-button-root {
    min-height: 44px;
    grid-column: 1 / -1;
}

.governance-inbox-row-heading .mud-typography-h6 {
    overflow-wrap: anywhere;
}

.governance-inbox-detail {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--mud-palette-lines-default, rgba(0, 0, 0, 0.12));
}

.governance-inbox-detail-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    align-items: start;
}

.governance-term-context {
    padding: 0.75rem;
    border-radius: 8px;
    background: var(--mud-palette-background-gray, rgba(0, 0, 0, 0.04));
}

@media (min-width: 768px) {
    /* The toggle returns to the summary line once there's width for a third column. */
    .governance-inbox-row-summary {
        grid-template-columns: auto minmax(0, 1fr) auto;
    }

    .governance-inbox-row-toggle.mud-button-root {
        grid-column: auto;
    }
}

@media (min-width: 1100px) {
    /* Desktop: the per-term context sits BESIDE the decision affordances. */
    .governance-inbox-detail-grid {
        grid-template-columns: minmax(15rem, 0.8fr) minmax(0, 1.7fr);
    }
}
/* FB-91 governance inbox mobile-first — end */

/* FB-99 review-surface bulk mode mobile-first — start */
/* Bulk mode (FB-95 V63.2): the pending inbox folded into signature-group batch cards. Authored mobile-first like the
   blocks above — base rules target 375px so the member table stacks/scrolls inside its own .descriptor-review-scroll-x
   container (FB-99: never a page-wide horizontal scroll), the batch header wraps, and the confirm overlay is a
   full-width sheet — and only min-width queries relax it. Everything stays inside the clipped admin canvas
   (FB-86 containment rule): max-width: 100% / min-width: 0 down the chain. Layout only. */
.descriptor-bulk-list,
.descriptor-bulk-card,
.descriptor-bulk-report,
.descriptor-batch-card,
.descriptor-batch-head,
.descriptor-batch-members,
.descriptor-batch-actions {
    max-width: 100%;
    min-width: 0;
}

.descriptor-bulk-progress {
    margin-bottom: 1rem;
}

/* V64.2 group paging: the loaded-vs-total readout and the "Load more groups" control under the batch list. */
.descriptor-bulk-readout {
    margin-bottom: 0.5rem;
}

.descriptor-bulk-load-more {
    display: flex;
    justify-content: center;
    margin-top: 1rem;
}

/* V64.2 member virtualization: the member table needs a bounded scroll viewport for <Virtualize> to window its rows
   (a group can hold hundreds — the operator cluster thousands). Cap the height and scroll INSIDE the card, keeping the
   FB-99 no-page-wide-scroll containment. */
.descriptor-batch-members {
    max-height: 60vh;
    overflow-y: auto;
}

.descriptor-bulk-report {
    margin-bottom: 1rem;
    padding: 0.75rem;
    border-radius: 8px;
    background: var(--mud-palette-background-gray, rgba(0, 0, 0, 0.04));
}

/* DG3.3 reason groups: the batch outcome renders one collapsible group per reason (count + heading), replacing the flat
   per-row wall. The header is a full-width, tappable toggle; the rows sit indented under it and hide when collapsed. */
.descriptor-bulk-report-group {
    border-top: 1px solid var(--mud-palette-lines-default, rgba(0, 0, 0, 0.08));
    padding: 0.25rem 0;
}

.descriptor-bulk-report-group:first-of-type {
    border-top: none;
}

.descriptor-bulk-report-group-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.4rem 0;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    color: inherit;
    font: inherit;
}

.descriptor-bulk-report-group-count {
    font-weight: 600;
    min-width: 2ch;
}

.descriptor-bulk-report-group-reason {
    overflow-wrap: anywhere;
}

.descriptor-bulk-report-group-rows {
    padding-left: 1.75rem;
}

.descriptor-bulk-report-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.15rem;
    padding: 0.35rem 0;
}

.descriptor-bulk-report-failed {
    color: var(--mud-palette-error-text, #b00020);
}

/* DG3.4: a "needs a source" group is re-actionable, not a failure — warning tone, distinct from the error-red failures. */
.descriptor-bulk-report-needs-source {
    color: var(--mud-palette-warning-text, #b26a00);
}

.descriptor-bulk-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}

.descriptor-batch-card {
    padding: 1rem;
    border: 1px solid var(--mud-palette-lines-default, rgba(0, 0, 0, 0.12));
    border-radius: 8px;
}

.descriptor-batch-head {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 0.75rem;
}

.descriptor-batch-header {
    overflow-wrap: anywhere;
}

/* V64.4: the signature info affordance sits at the end of the header row — a quiet, tappable target that explains
   why the rows are grouped and what each header segment means. */
.descriptor-batch-signature-tip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
}

.descriptor-batch-signature-info {
    cursor: help;
}

/* V64.4: the "what will happen" sentence beneath the confirm overlay's exact counts. */
.descriptor-bulk-confirm-effect {
    overflow-wrap: anywhere;
    opacity: 0.85;
}

/* The member table is contained in its own horizontal-scroll wrapper — below the breakpoint it scrolls INSIDE the
   card, never pushing the shell past the admin canvas. */
.descriptor-batch-members {
    overflow-x: auto;
}

.descriptor-batch-members table {
    width: 100%;
    border-collapse: collapse;
}

.descriptor-batch-members th,
.descriptor-batch-members td {
    padding: 0.4rem 0.5rem;
    text-align: left;
    vertical-align: top;
}

.descriptor-batch-members input[type="checkbox"] {
    width: 22px;
    height: 22px;
    min-width: 22px;
    min-height: 22px;
}

.descriptor-batch-excluded {
    opacity: 0.6;
}

.descriptor-batch-actions {
    margin-top: 0.75rem;
}

.descriptor-batch-apply.mud-button-root {
    min-height: 44px;
    width: 100%;
}

/* Base = 375px: the confirm step is a full-bleed bottom sheet with 44px actions. */
.descriptor-bulk-confirm-overlay {
    position: fixed;
    inset: 0;
    z-index: 1400;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: rgba(0, 0, 0, 0.4);
}

.descriptor-bulk-confirm {
    width: 100%;
    max-width: 32rem;
    padding: 1.25rem;
    display: grid;
    gap: 0.75rem;
}

.descriptor-bulk-confirm-counts {
    overflow-wrap: anywhere;
}

.descriptor-bulk-confirm-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: flex-end;
}

.descriptor-bulk-confirm-actions .mud-button-root {
    min-height: 44px;
}

@media (min-width: 768px) {
    /* Once there's width the batch actions sit inline and the confirm sheet centers as a dialog. */
    .descriptor-batch-apply.mud-button-root {
        width: auto;
    }

    .descriptor-bulk-confirm-overlay {
        align-items: center;
    }
}
/* FB-99 review-surface bulk mode mobile-first — end */

/* DG3.2 catalog source provenance mobile-first — start */
/* The catalog browse card lists a term's REAL sources and demotes the canon-verification stamp to an event line.
   Base rules target 375px: the source rows wrap and break long URLs so nothing pushes the card past the clipped
   admin canvas (FB-86 containment); the stamp line reads as a quiet verified-by/when caption. Scoped to the
   descriptor-source-* classes only; no max-width query needed — the rules hold at 768/1280 unchanged. */
.descriptor-source-list {
    padding: 0;
    max-width: 100%;
    min-width: 0;
}
.descriptor-source-item {
    overflow-wrap: anywhere;
    word-break: break-word;
}
.descriptor-source-stamp {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    opacity: 0.75;
}
/* DG3.2 catalog source provenance mobile-first — end */

/* FB-91 descriptor library mobile-first — start */
/* The category/connection library pages (V61.4 decomposition of AdminDescriptorCategories/-Relationships),
   authored mobile-first like this file's FB-92 block: base rules target 375px — metrics, cards, and the
   filter/form panels stack to one full-width column with 44px touch targets — and only min-width queries layer
   the desktop grids back on. Everything stays inside the clipped admin canvas (FB-86 containment rule):
   max-width: 100% / min-width: 0 down the chain. Scoped to the descriptor-library-* classes only. */
.descriptor-library-section,
.descriptor-library-section .admin-metric-grid,
.descriptor-library-section .admin-workspace-grid,
.descriptor-library-section .admin-workspace-card,
.descriptor-library-section .admin-workspace-card > *,
.descriptor-library-section .tool-access-filter-panel {
    max-width: 100%;
    min-width: 0;
}

/* Base = 375px: metrics, library cards, and the filter/form panels all stack to one full-width column. */
.descriptor-library-metric-grid.admin-metric-grid,
.descriptor-library-card-grid.admin-workspace-grid,
.descriptor-library-filter-panel.tool-access-filter-panel {
    grid-template-columns: 1fr;
}

.descriptor-library-filter-panel .tool-access-filter-summary {
    min-width: 0;
}

/* A long category or relationship title breaks instead of forcing its card wider than the viewport. */
.descriptor-library-section .admin-workspace-card .mud-typography-h6 {
    overflow-wrap: anywhere;
}

/* 44px touch floor on the card review/save actions and the workspace back-links. */
.descriptor-library-section .descriptor-review-actions .mud-button-root,
.descriptor-library-section .descriptor-library-filter-panel .mud-button-root {
    min-height: 44px;
}

.descriptor-library-term-link {
    display: inline-block;
    padding: 0.5rem 0;
}

/* DG3.5 Explorer: a store-mismatch row (a descriptor in only one store) gets a left accent so the health chip
   isn't the only signal, and the caption lines wrap instead of forcing the card wider than a 375px viewport. */
.descriptor-explorer-row--mismatch {
    border-left: 3px solid var(--mud-palette-warning);
}

.descriptor-explorer-aliases,
.descriptor-explorer-sources {
    overflow-wrap: anywhere;
}

/* DG3.6 Explorer where-used pane: the FB-91 context panel folded under an ops-backed row (linked menu items +
   recipe usage). Full-width under the card content and contained at 375px so it never forces the card wider. */
.descriptor-explorer-where-used {
    margin-top: 0.75rem;
    min-width: 0;
}

@media (min-width: 640px) {
    /* Layer the shared multi-column grids back on once there's width to spare. */
    .descriptor-library-metric-grid.admin-metric-grid {
        grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    }

    .descriptor-library-card-grid.admin-workspace-grid {
        grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    }

    .descriptor-library-filter-panel.tool-access-filter-panel {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 1100px) {
    /* The filter/form panels return to the full desktop row. */
    .descriptor-library-filter-panel.tool-access-filter-panel {
        grid-template-columns: minmax(16rem, 1.4fr) minmax(12rem, 0.8fr) minmax(12rem, 0.8fr) auto;
    }
}
/* FB-91 descriptor library mobile-first — end */

/* FB-93 admin area sweep mobile-first — start */
/* The area-wide sweep. The admin table pages' MudTables already self-contain (MudBlazor's .mud-table-container
   scrolls horizontally and the default Xs breakpoint stacks rows on phones), so these page-scoped rules repair
   only the regions the audit found escaping a 375px viewport: billing's fixed three-/two-column card grids and
   the white-glove intake internals (a field grid fixed at three columns and an account-link command row whose
   14rem-plus-button template outgrows a phone). Authored mobile-first like the blocks above: base rules target
   375px, ONLY min-width queries layer the desktop columns back on, and everything stays inside the clipped
   admin canvas (FB-86 containment): max-width: 100% / min-width: 0 down the chain. Scoped to the
   admin-billing-console / admin-white-glove-console page classes only. Layout only — no behavior changes. */
.admin-billing-console .admin-list-card,
.admin-billing-console .admin-list-card > .mud-table,
.admin-white-glove-console .admin-intake-card,
.admin-white-glove-console .admin-intake-card > .mud-table {
    max-width: 100%;
    min-width: 0;
}

/* Base = 375px: billing's roadmap/function card grids stack to one readable column. */
.admin-billing-console .admin-roadmap-grid,
.admin-billing-console .admin-function-grid {
    grid-template-columns: 1fr;
}

/* Base = 375px: the intake field grid stacks, and the account-link command row stacks its select over its
   button instead of holding the desktop minmax(14rem, 1fr) auto template. */
.admin-white-glove-console .support-ticket-field-grid,
.admin-white-glove-console .operational-link-command {
    grid-template-columns: minmax(0, 1fr);
}

/* The intake heading wraps its status pill under the title instead of pushing past the card edge. */
.admin-white-glove-console .admin-intake-head {
    flex-wrap: wrap;
}

/* Long shared-folder/website values, hashes, and file names break instead of forcing the card wide. */
.admin-white-glove-console .support-ticket-field,
.admin-white-glove-console .intake-file-table .mud-table-cell {
    overflow-wrap: anywhere;
}

@media (min-width: 640px) {
    /* Layer the two-across layouts back on once there's width to spare. */
    .admin-billing-console .admin-roadmap-grid,
    .admin-white-glove-console .support-ticket-field-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .admin-white-glove-console .operational-link-command {
        grid-template-columns: minmax(14rem, 1fr) auto;
    }
}

@media (min-width: 960px) {
    /* Full desktop column counts. */
    .admin-billing-console .admin-function-grid,
    .admin-white-glove-console .support-ticket-field-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
/* FB-93 admin area sweep mobile-first — end */

/* FB-95 review-desk focus mode + keyboard mobile-first — start */
/* The V63.3 Review Desk shell: a Bulk ↔ Focus mode switch, Focus mode's always-in-view decision bar (a sticky
   bottom sheet on phone, a fixed side rail on desktop — the reviewer never scrolls to reach Verify), the roving
   keyboard focus ring shared by both modes, and the `?` keyboard-help overlay. Authored mobile-first like the blocks
   above: base rules target 375px and ONLY min-width queries layer the desktop rail on; everything stays inside the
   clipped admin canvas (FB-86 containment) with 44px touch targets. Scoped to the descriptor-review-desk /
   descriptor-focus-* / descriptor-key-help-* classes only. */
.descriptor-review-desk,
.descriptor-focus-shell,
.descriptor-focus-stage,
.descriptor-focus-card,
.descriptor-focus-card > * {
    max-width: 100%;
    min-width: 0;
}

/* The mode switch reads as a two-tab segmented control; the buttons hold a 44px touch floor. */
.descriptor-review-mode-switch {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.descriptor-review-mode-switch .mud-button-root {
    min-height: 44px;
}

/* The focus shell (and each roving-focused card) shows a clear ring so keyboard position is never ambiguous. */
.descriptor-focus-shell:focus,
.descriptor-focus-shell:focus-visible {
    outline: 2px solid var(--mud-palette-primary, #594ae2);
    outline-offset: 2px;
    border-radius: 8px;
}

.descriptor-batch-card-focused {
    outline: 2px solid var(--mud-palette-primary, #594ae2);
    outline-offset: 2px;
}

.descriptor-focus-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.descriptor-focus-filter {
    flex: 1 1 8rem;
    min-width: 0;
    min-height: 44px;
    padding: 0 0.75rem;
    border: 1px solid var(--mud-palette-lines-inputs, rgba(0, 0, 0, 0.3));
    border-radius: 8px;
    font: inherit;
}

/* Base = 375px: the card stacks above a sticky bottom action bar (bottom-sheet feel) so Verify is always reachable
   without scrolling the page. */
.descriptor-focus-stage {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.descriptor-focus-card-head {
    display: flex;
    gap: 0.5rem;
    align-items: flex-start;
}

.descriptor-focus-term {
    overflow-wrap: anywhere;
}

.descriptor-focus-actionbar {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.5rem 0;
    background: var(--mud-palette-surface, #fff);
}

.descriptor-focus-actionbar .mud-button-root {
    flex: 1 1 auto;
    min-height: 44px;
}

/* The `?` help overlay, the group-verify confirm, and the V64.4 "how governance works" explainer dim the surface and
   sheet up from the bottom on phone. */
.descriptor-key-help-overlay,
.descriptor-gov-help-overlay,
.descriptor-focus-confirm-overlay {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.45);
}

.descriptor-key-help,
.descriptor-gov-help,
.descriptor-focus-confirm {
    width: 100%;
    max-width: 30rem;
    padding: 1rem;
    border-radius: 12px;
}

/* The explainer runs longer than the keyboard list — cap its height and scroll inside the sheet on a short screen. */
.descriptor-gov-help {
    max-height: 85vh;
    overflow-y: auto;
}

.descriptor-key-help-head,
.descriptor-gov-help-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

/* V64.4 explainer body: stacked heading/body points and a per-kind "what am I deciding" list. */
.descriptor-gov-help-points {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin: 0.5rem 0;
}

.descriptor-gov-help-kinds {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0.5rem 0 0;
}

.descriptor-gov-help-kind-label {
    font-weight: 600;
}

.descriptor-gov-help-kind-desc {
    margin: 0.1rem 0 0;
    opacity: 0.85;
}

.descriptor-key-help-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin: 0.5rem 0;
}

.descriptor-key-help-row {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
}

.descriptor-key-help-kbd {
    flex: 0 0 5rem;
    padding: 0.15rem 0.4rem;
    border: 1px solid var(--mud-palette-lines-default, rgba(0, 0, 0, 0.2));
    border-radius: 6px;
    font-family: ui-monospace, monospace;
    text-align: center;
}

.descriptor-focus-confirm-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: flex-end;
    margin-top: 0.75rem;
}

.descriptor-focus-confirm-actions .mud-button-root {
    min-height: 44px;
}

@media (min-width: 768px) {
    /* Once there's width, Focus mode's action bar becomes a fixed side rail beside the card — always in view without
       ever covering the row content — and the overlays center as dialogs. */
    .descriptor-focus-stage {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(13rem, 17rem);
        align-items: start;
        gap: 1rem;
    }

    .descriptor-focus-actionbar {
        position: sticky;
        top: 1rem;
        bottom: auto;
        flex-direction: column;
        padding: 0;
        background: transparent;
    }

    .descriptor-key-help-overlay,
    .descriptor-gov-help-overlay,
    .descriptor-focus-confirm-overlay {
        align-items: center;
    }
}
/* FB-95 review-desk focus mode + keyboard mobile-first — end */

/* UI2.5 — show-once staff PIN display: the spaced-out, heavy plaintext styling that was an inline Style on
   the MudText, moved to a page-scoped class so the typography stays MudText Typo-driven. */
.employee-pin-display {
    letter-spacing: .3em;
    font-weight: 800;
}

/* V69.5 bolt-on switchboard — mobile-first. The two environment panels stack on a phone and sit side by side
   from the tablet breakpoint up: staging and production are independent decisions, so they are never presented
   as one control, at any width. */
.feature-switch-row__envs {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-top: .75rem;
}

.feature-switch-env {
    flex: 1 1 0;
    padding: .75rem;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--mud-default-borderradius);
}

/* The environment THIS host serves — the one whose "live" state is the reader's own. */
.feature-switch-env--current {
    border-color: var(--mud-palette-info);
}

.feature-switch-env__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    min-height: 2rem;
}

.feature-switch-env__commitment {
    margin: .5rem 0;
}

/* 44px minimum touch targets: these controls suspend and retract live features — a mis-tap is expensive. */
.feature-switch-env .feature-switch-action,
.feature-switch-env .feature-switch-toggle {
    min-height: 44px;
}

.feature-switch-env .feature-switch-action {
    margin-top: .5rem;
    margin-right: .5rem;
}

@media (min-width: 768px) {
    .feature-switch-row__envs {
        flex-direction: row;
    }
}

/* UW.1 QR builder/designer — these rules live here because app.css sits at its enforced byte cap
   (audit-public-surface.mjs); admin.css loads globally right after it, so equal-specificity rules below win. */

/* Unlabelled minor ruler ticks: quarter-inch / half-centimetre short lines between the numbered marks,
   positioned by the same inline percent stops the numbered ticks use. */
.qr-designer-ruler-tick--minor {
    background: rgba(8, 16, 29, 0.35);
}

.qr-designer-ruler--top .qr-designer-ruler-tick--minor {
    bottom: 0;
    height: 0.26rem;
    width: 1px;
}

.qr-designer-ruler--left .qr-designer-ruler-tick--minor {
    height: 1px;
    right: 0;
    width: 0.26rem;
}

/* The custom designer's visual-style skin: the same .qr-template-surface the stock preview paints its six
   styles on, laid absolute UNDER the positioned blocks with the stock padding zeroed — blocks keep their
   percent-of-CANVAS coordinate contract, so the skin must never become their offset or padded box. */
.qr-designer-canvas > .qr-template-surface {
    inset: 0;
    padding: 0;
    position: absolute;
}

/* Spiral text consumes the template-level styling like every other preset field: SVG glyphs paint with fill
   (not color), so the paint follows currentColor and the same inline color:… token restyles the on-path text. */
.qr-template-language-spiral-text {
    color: var(--qr-template-style-ink);
    fill: currentColor;
}

/* FB-71 dark-mode chrome overrides — start
   Scope: body.theme-dark, set by TmaThemeProviderHost (body, not the shell div, so dialogs/popovers
   that render outside the layout are covered). ONLY the UNPAIRED rules live here — app.css chrome text
   whose fixed light-mode color sits on a Mud-themed surface that flips dark (the two drawers, dialog
   copy). Self-paired panels (the public marketing shell, the local-menus surface, the white admin
   cards) set BOTH their background and text and stay deliberately light — legible in either mode —
   so they are intentionally not re-themed here. These rules live in admin.css because app.css sits
   at its enforced byte cap (audit-public-surface.mjs); admin.css loads globally right after it. */
/* SHELL.6: the WORKSPACE drawer came off this pair. It is no longer a Mud-themed surface — it is the
   design's own panel, and it takes its ink from the design tokens, which are defined per theme rather than
   patched after the fact. This rule could never have covered it anyway: theme-dark is set on <body> and the
   marketing shell sets no such class, so the patch was silent exactly where the drawer was worst. */
body.theme-dark .admin-nav-drawer {
    color: var(--mud-palette-drawer-text, #ffffff);
}

body.theme-dark .admin-drawer-audience-title {
    border-top-color: var(--mud-palette-divider, #486178);
    color: var(--mud-palette-secondary, #33cccc);
}

/* The tool-status hints render directly on the drawer surface; their light-mode greens/ambers land
   near-invisible on the dark drawer, so they re-resolve through the palette's dark-legible accents. */
body.theme-dark .tool-nav-status.is-enabled {
    color: var(--mud-palette-success, #33cccc);
}

body.theme-dark .tool-nav-status.is-coming-soon {
    color: var(--mud-palette-warning, #f0b45c);
}

body.theme-dark .tool-nav-status.is-hidden {
    color: var(--mud-palette-text-disabled, #8ca2b4);
}

/* Checkout hand-off copy sits on a bare MudPaper (themed surface) but pinned the light-ink vars. */
body.theme-dark .checkout-account-handoff-copy .mud-typography-subtitle1 {
    color: var(--mud-palette-text-primary, #ffffff);
}

body.theme-dark .checkout-account-handoff-copy .mud-typography-body2 {
    color: var(--mud-palette-text-secondary, #c1cfdd);
}
/* FB-71 dark-mode chrome overrides — end */

/* NEW.1 (F-396 follow-up, owner-reported 2026-08-15) — start
   .app-canvas (every is-app-preview route's content wrapper — app.css) and .tma-shell-main's
   vacated-drawer gutter (MainLayoutContent.razor.css, the NEW.6 fix) both consume this ONE token,
   each keeping its own light-mode hex as the var() fallback so light mode is provably unchanged.
   This is the only place that ever assigns it — one source, so the two sides cannot re-drift.
   Sourced from MudBlazor's own dark palette (TmaTheme.cs's PaletteDark.Background) rather than a
   second hand-picked literal, per the same reasoning the FB-71 block above already applies. */
body.theme-dark {
    --app-preview-canvas-bg: var(--mud-palette-background, #111b28);
    /* NEW.8 (F-569, owner ruling 2026-08-15): the two surface tokens the card/tab-bar block below consumes.
       --admin-card-bg pairs the eleven #ffffff cards + the two tab bars with MudBlazor's own Surface slot
       (TmaTheme.cs's PaletteDark.Surface); --admin-card-subtle-bg pairs .task-type-options's tinted #f7fbfd
       with the dark BackgroundGray slot. Same one-assignment-point reasoning as --app-preview-canvas-bg
       above: app.css only ever consumes these, never redeclares them. */
    --admin-card-bg: var(--mud-palette-surface, #2c3e50);
    --admin-card-subtle-bg: var(--mud-palette-background-gray, #1c2c3d);
}
/* NEW.1 dark-mode canvas token — end */

/* NEW.8 (F-569, owner ruling 2026-08-15) — start
   The ink half of the card/tab-bar fix: every F-569 surface's descendant text/border rules in app.css
   already read --tma-text / --tma-subtle / --tma-border (never a literal), so pairing the ink is this ONE
   re-assignment of those three custom properties, scoped to exactly the F-569 selector list. Custom
   properties inherit, so every descendant — MudBlazor typography classes, the tabbar's own tab labels —
   picks up dark-legible ink with no further app.css edit, and no OTHER white surface (the FB-71 comment
   above names them deliberately still-light) is touched by this selector list. Sourced from MudBlazor's
   dark palette (TmaTheme.cs's PaletteDark.TextPrimary/TextSecondary/Divider). */
body.theme-dark .tool-panel,
body.theme-dark .tool-preview-card,
body.theme-dark .intake-form,
body.theme-dark .metric-card,
body.theme-dark .challenge-prompt,
body.theme-dark .task-card,
body.theme-dark .employee-task-card,
body.theme-dark .task-template-card,
body.theme-dark .social-account-card,
body.theme-dark .social-menu-item-card,
body.theme-dark .performance-card,
body.theme-dark .video-tabs,
body.theme-dark .task-tabs,
body.theme-dark .task-type-options {
    --tma-text: var(--mud-palette-text-primary, #ffffff);
    --tma-subtle: var(--mud-palette-text-secondary, #c1cfdd);
    --tma-border: var(--mud-palette-divider, #486178);
}
/* NEW.8 dark-mode card/tab ink pairing — end */

/* NEW.3 (owner ruling 2026-08-15) — the operator footer had NO dark-mode rule at all: it painted
   var(--tma-blue) — a light-mode-only literal — in both themes. The public/marketing shell's own
   footer already themes correctly (--public-header-bg redefines under :root[data-theme="dark"]);
   this rule's lower specificity (2 classes) than that scoped one (3 classes) means it only ever
   reaches the operator footer, never overriding the public shell's own answer. */
body.theme-dark .site-footer {
    background: var(--mud-palette-background, #111b28);
}

/* NEW.3 dark-mode contrast sweep (owner report 2026-08-17) — start
   Owner walked home / pricing / for-restaurants / signup-checkout / thank-you / access-denied / the dashboard
   shell in dark mode and reported bright-white objects and illegible text. MEASURED, surface by surface, what
   was actually still theme-deaf on those seven routes rather than sweeping all 184 white literals in app.css:
     - the marketing shell already themes (app.css:1441-1600 repaints every public card/toggle/plan surface
       from --public-*, and app.css:13328 flips those under :root[data-theme="dark"]);
     - /signup-checkout and /thank-you were re-cut onto tokens.css by W1a/W1c (their scoped sheets) — the ONE
       control they missed is the address well, fixed in CheckoutEmailCorrectionForm.razor.css;
     - /access-denied paints .admin-access-panel (no colour of its own) inside .tool-panel, already covered by
       the NEW.8 block above;
   which leaves the two families below. Everything else white in app.css is either already dark-gated, or a
   print/export/QR-paper literal that never renders in-app, or dead (no .razor emits .pricing-plan-card,
   .roi-calculator, .app-coming-soon-banner today).

   WHY THE OVERRIDES ARE HERE AND NOT var()-IN-app.css, which is the --app-preview-canvas-bg pattern this
   sweep otherwise follows: app.css is 358,374 bytes against the 358,400-byte public-surface cap
   (scripts/audit-public-surface.mjs) — 26 bytes of headroom, which is less than ONE `var(--token, #ffffff)`
   substitution. That is the same constraint the FB-71 header above records, and the same answer it reached.
   Each rule below therefore states the dark value directly on a dark-only selector, sourced from tokens.css's
   measured dark ramp or from the --admin-card-bg token NEW.8 already defined — never a new hand-picked hex —
   so light mode is untouched by construction (no rule below can ever match in light) and there is still one
   assignment point per surface. */

/* The dashboard shell's card family (app.css:10379-10394, one rule, eleven selectors) painted #ffffff in both
   themes. Its ink already reads --tma-text/--tma-subtle/--tma-border, so re-assigning those three custom
   properties on the same selector list pairs every descendant at once — the NEW.8 technique, applied to the
   surfaces NEW.8 deliberately left out. --admin-card-bg is reused rather than re-derived so the dashboard's
   cards and the operator console's cards cannot drift to two different darks. */
body.theme-dark .admin-metric-card,
body.theme-dark .admin-list-card,
body.theme-dark .admin-roadmap-card,
body.theme-dark .admin-function-card,
body.theme-dark .admin-support-panel,
body.theme-dark .admin-workspace-card,
body.theme-dark .admin-config-card,
body.theme-dark .admin-filter-panel,
body.theme-dark .support-ticket-list-panel,
body.theme-dark .support-ticket-detail-panel,
body.theme-dark .account-admin-card {
    background: var(--admin-card-bg, #2c3e50);
    --tma-text: var(--mud-palette-text-primary, #ffffff);
    --tma-subtle: var(--mud-palette-text-secondary, #c1cfdd);
    --tma-border: var(--mud-palette-divider, #486178);
}

/* The two metric readouts inside those cards are the illegibility the owner named: --tma-blue (#2980b9) on the
   dark card measures 2.6:1, under the 4.5:1 floor. Re-pointed at tokens.css's dark accent ink (#6FB9E8, 5.1:1
   on the same card) — as a TARGETED rule rather than by re-assigning --tma-blue on the card, because that
   token is also a BACKGROUND for white-on-blue pills elsewhere in the console, which lightening would break. */
body.theme-dark .admin-metric-card strong,
body.theme-dark .admin-list-card li strong {
    color: var(--accent-fg, #6fb9e8);
}

/* The promotion card on /pricing and the signup plan builder (app.css:2429): a fixed #f4fbff panel, so it
   stayed a pale slab on the dark page — the only surface in the public plan-builder vocabulary that the
   is-public-site block does not repaint. --surface-2 is tokens.css's inset-region slot, which is what this
   panel is (a tinted well inside .pricing-builder). :root[data-theme="dark"] rather than body.theme-dark: the
   public routes are static-SSR and theme-boot.js stamps data-theme before first paint. The
   :not(.is-locked-light) guard mirrors app.css:13328's own dark block, so a route that later pins itself light
   keeps its light palette here too. */
:root[data-theme="dark"] .site-shell:not(.is-locked-light) .pricing-promo-card {
    background: var(--surface-2, #1e2e42);
    border-color: var(--hairline-strong, rgba(236, 240, 241, 0.22));
    --tma-text: var(--ink-1, #edf2f6);
    --tma-subtle: var(--ink-2, #b9c6d3);
    --tma-blue: var(--accent-fg, #6fb9e8);
}

/* The language-suggestion prompt (app.css:13236-13290) is appended to <body> by i18n.js on EVERY public route,
   and it pinned #ffffff over #10212b ink — a white slab floating over the dark page, which is the most visible
   instance of the owner's report because it is the one element that can appear on all seven walked routes. No
   .site-shell ancestor to scope through (it is a direct child of <body>), and no circuit needed to render it,
   so :root[data-theme="dark"] is both the correct and the only usable scope. */
:root[data-theme="dark"] .locale-prompt {
    background: var(--surface, #182535);
    border-color: var(--hairline-strong, rgba(236, 240, 241, 0.22));
    color: var(--ink-1, #edf2f6);
}

:root[data-theme="dark"] .locale-prompt p {
    color: var(--ink-2, #b9c6d3);
}

/* The accept button keeps its own white-on-fill ink; only the fill moves, to the accent that is measured
   against white in both themes. The dismiss button is the outlined twin and takes the panel's own ink. */
:root[data-theme="dark"] .locale-prompt button:first-child {
    background: var(--accent-strong, #1f6e9c);
    border-color: var(--accent-strong, #1f6e9c);
}

:root[data-theme="dark"] .locale-prompt button:last-child {
    border-color: var(--hairline-strong, rgba(236, 240, 241, 0.22));
    color: var(--ink-1, #edf2f6);
}
/* NEW.3 dark-mode contrast sweep — end */

/* FB-69 wallboard editor (WB.3) — start
   The editor's live preview reuses the TV display's block COMPONENTS (WallboardScreenBlocks) at reduced scale.
   The display styles itself inline in its own self-contained document (vh-based TV typography), so those rules
   never reach an admin page; this section restates the wb-* skin at pane scale, scoped under .wb-preview-frame
   so nothing leaks to the display or the rest of the console. */
.wb-preview-frame {
    background: #0b1020;
    color: #f4f6fb;
    border-radius: var(--mud-default-borderradius, 8px);
    padding: .75rem;
    overflow: hidden;
    width: 100%;
    max-width: 520px;
}

.wb-preview-frame .wb-blocks {
    display: flex;
    flex-direction: row;
    gap: .5rem;
    height: 100%;
    min-height: 0;
}

.wb-preview-frame.wb-portrait .wb-blocks {
    flex-direction: column;
}

.wb-preview-frame .wb-block {
    flex: 1 1 0;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.wb-preview-frame .wb-block--soldout {
    align-items: center;
    justify-content: center;
    text-align: center;
}

.wb-preview-frame .wb-count {
    font-size: 2.6rem;
    font-weight: 900;
    line-height: 1;
}

.wb-preview-frame .wb-count.is-clear { color: #4ade80; }

.wb-preview-frame .wb-count.has-items { color: #f87171; }

.wb-preview-frame .wb-label {
    font-size: .8rem;
    font-weight: 700;
    color: #cbd5f5;
}

.wb-preview-frame .wb-menu-name,
.wb-preview-frame .wb-matrix-name {
    font-size: 1rem;
    font-weight: 900;
    margin: 0 0 .25rem;
    overflow-wrap: anywhere;
}

.wb-preview-frame .wb-section-name {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .14em;
    color: #8ea2c8;
    margin: .5rem 0 .2rem;
}

.wb-preview-frame .wb-menu-items {
    list-style: none;
    margin: 0;
    padding: 0;
}

.wb-preview-frame .wb-menu-item { margin: 0 0 .35rem; }

.wb-preview-frame .wb-item-line {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: .5rem;
}

.wb-preview-frame .wb-item-name {
    font-size: .8rem;
    font-weight: 700;
    overflow-wrap: anywhere;
}

.wb-preview-frame .wb-item-price {
    font-size: .8rem;
    font-weight: 700;
    color: #4ade80;
    white-space: nowrap;
}

.wb-preview-frame .wb-item-desc {
    display: block;
    font-size: .7rem;
    color: #cbd5f5;
    overflow-wrap: anywhere;
}

.wb-preview-frame .wb-matrix-grid {
    border-collapse: collapse;
    width: 100%;
    font-size: .6rem;
}

.wb-preview-frame .wb-matrix-grid th,
.wb-preview-frame .wb-matrix-grid td {
    border: 1px solid #2b3a5e;
    padding: .15rem .25rem;
    text-align: center;
}

.wb-preview-frame .wb-matrix-grid th.wb-matrix-item {
    text-align: left;
    overflow-wrap: anywhere;
}

.wb-preview-frame .wb-cell-C,
.wb-preview-frame .wb-cell-R {
    font-weight: 700;
    color: #f87171;
}

.wb-preview-frame .wb-cell-M { color: #fbbf24; }

.wb-preview-frame .wb-cell-NC {
    font-style: italic;
    color: #fbbf24;
}

.wb-preview-frame .wb-matrix-legend {
    font-size: .6rem;
    color: #8ea2c8;
}

.wb-preview-frame .wb-unavailable {
    font-size: .85rem;
    color: #8ea2c8;
    margin: auto;
    text-align: center;
}

/* The editor's block cards: plain divs (native drag events bind directly), styled to sit with the Mud console. */
.wb-editor-block {
    border: 1px solid var(--mud-palette-lines-default, #ddd);
    border-radius: var(--mud-default-borderradius, 8px);
    padding: .75rem;
    margin-top: .5rem;
    background: var(--mud-palette-surface, #fff);
    cursor: grab;
}

.wb-editor-block.is-dragging {
    opacity: .6;
}
/* FB-69 wallboard editor (WB.3) — end */

/* FB-117 menu-embed snippet (WB.6): the copy-paste code block on the menu builder. Mobile-first — the
   snippet wraps instead of forcing horizontal scroll at 375px, and stays a readable code block wider up. */
.tma-embed-snippet {
    white-space: pre-wrap;
    word-break: break-all;
    font-size: .8rem;
    line-height: 1.4;
    padding: .75rem;
    border: 1px solid var(--mud-palette-lines-default, #ddd);
    border-radius: var(--mud-default-borderradius, 8px);
    background: var(--mud-palette-background-gray, #f5f5f5);
    max-width: 100%;
    overflow-x: auto;
}

/* F-169 order-queue card chips — start */
/* An order's situation on the queue board is TWO chips, not one: the front-of-house status and — only where
   the track applies — the kitchen ticket. Mobile-first: at 375px the pair wraps under the order reference
   instead of pushing the card into horizontal scroll, and a long reference breaks rather than widening it. No
   max-width query: the rules hold unchanged at 768/1280. Emphasis is deliberately never colour-only — the two
   open states also carry weight here, and an icon plus a label in the markup — because the board is read across
   a pass under service pressure. F-629 NEW.1: the chips render through the kit's TmaVerdictChip now, so colour
   comes from the shared TmaVerdictTone table (still no raw hex here — TmaVerdictChip's own scoped sheet owns
   the tone/border declarations, incl. the extra emphasis border Requested alone carries). Admin-only surface,
   so this lives in admin.css — app.css is at its enforced public-surface byte cap (F-191). */
.order-queue-card-chips {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 0.25rem;
    min-width: 0;
    max-width: 100%;
}

.order-queue-card-reference {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Requested and Needs Edit are the only states waiting on a human. Requested is the filled (verdict-emphasized)
   chip; this lifts BOTH above the settled chips by weight, so Needs Edit still outranks Accepted/Complete/
   Rejected without being emphasized like a brand-new order nobody has looked at. */
.order-queue-status-chip--action {
    font-weight: 600;
}

.order-queue-kitchen-chip {
    max-width: 100%;
}
/* F-169 order-queue card chips — end */

/* F-173 order page (own-page order view + prev/next) — start */
/* The order now opens on its own route, so the walk bar and the decision buttons are the two things that have to
   work under a thumb mid-service. Mobile-first and unconditional: every rule below holds at 375, 768 and 1280 —
   there is no max-width gate that could drop the touch floor on a tablet. Everything wraps rather than widening,
   because a decision an operator has to scroll SIDEWAYS to reach is a decision they will not take. Admin-only
   surface, so this lives in admin.css: app.css is at its enforced public-surface byte cap (F-191). */
.order-detail-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    max-width: 100%;
}

.order-detail-nav-cycle {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
    max-width: 100%;
}

/* The walk bar AND every decision clear the 44px touch floor (docs/05 section 4 - WCAG 2.5.5) via min-height,
   never a fixed height, which would fight the label centering. The back link is included deliberately: measured
   at 375px it renders 42px on MudBlazor's text-button padding alone, which is under the floor. */
.order-detail-nav .mud-button-root,
.order-detail-cycle-button,
.order-detail-actions .mud-button-root {
    min-height: 44px;
}

.order-detail-position {
    min-width: 0;
    white-space: nowrap;
}

.order-detail-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
    max-width: 100%;
}

/* The seating picker shares the action row; it grows on a wide screen and drops to its own line on a phone
   instead of pushing the Confirm button off the edge. */
.order-detail-actions .mud-input-control {
    flex: 1 1 12rem;
    min-width: 0;
}

.order-detail-panel {
    min-width: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
}

/* W2a-restyle (design-w2a-restyle, F-544 split) — start */
/* The shared AdminEmptyState shell (My Dashboard's first-run welcome + launch checklist heading, Items'
   own first-run welcome, and every plain "no rows" empty state across the admin console) re-skinned to
   the design system's dashed-frame `.empty` pattern, using tokens.css values only — one component, one
   rule set, so every admin page that already renders <AdminEmptyState> or the literal first-run block
   picks this up without a page-by-page pass. Scoped to admin-empty-state/admin-first-run* only. */
.admin-empty-state {
    display: grid;
    place-items: center;
    gap: var(--sp-3);
    text-align: center;
    padding: var(--sp-9) var(--sp-5);
    border: 1.5px dashed var(--hairline-strong);
    border-radius: var(--radius);
    color: var(--ink-3);
}

.admin-empty-state .mud-icon-root {
    width: 34px;
    height: 34px;
}

.admin-empty-state h2 {
    font-weight: 800;
    color: var(--ink-2);
}

.admin-empty-state p {
    max-width: 40ch;
    color: var(--ink-2);
}

/* The first-run welcome is a filled card rather than a dashed placeholder frame — it is guiding a real,
   live action (guided setup / create a concept), not standing in for missing data. */
.admin-first-run {
    border-style: solid;
    background: var(--surface);
    box-shadow: var(--elev-1);
}

.admin-first-run-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--sp-3);
    margin-top: var(--sp-2);
}
/* W2a-restyle — end */

/* The queue card is a link now (it opens the order page), so it answers the keyboard and says so visibly. */
.order-queue-card:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}
/* F-173 order page (own-page order view + prev/next) — end */

/* NEW.1 (F-191): the four blocks below moved here from app.css verbatim — app.css was within 64 bytes of the
   358,400-byte public-surface cap (audit-public-surface.mjs), and every one of these selectors is scoped to an
   admin-console page (AdminItemEditor, AdminOrderQueue, admin-only authoring rows, /admin/descriptor-review),
   so admin.css — loaded right after app.css on every route — is the correct home, same precedent as DG3.2. */

/* AR.1 item-editor mobile-first — start */
/* The per-item editor (AdminItemEditor + its recipe/pricing/nutrition/allergen/… tabs) is authored on phones as much as
   on desktops. These rules are authored mobile-first: the base targets a 375px viewport (self-contained scroll, single
   column, 44px touch targets), and a min-width query layers the roomier desktop treatment on top. Everything is scoped
   to .admin-item-editor / .item-editor-tabs so no other admin console is affected. Layout only — no behaviour changes.

   F-622 NEW.3: the tab strip retired MudTabs onto TmaTabs (the kit's plain .tabs/.tab strip,
   Tma.Surface.Components/Design/TmaTabs.razor.css) — its own sheet already carries `overflow-x: auto` and
   `scrollbar-width: none` on `.tabs` (the block that used to live here targeting `.mud-tabs-toolbar` /
   `.mud-tabs-toolbar-wrapper` / `.mud-tabs-tabbar` / the toolbar-content/-inner descendants / the now-nonexistent
   `.mud-tabs-scroll-button`), so those rules are gone rather than retargeted — nothing here needs to restate a
   behaviour the kit component already guarantees everywhere it's used. What remains is genuinely PAGE-specific:
   the exact tab-strip touch-target height, the edge-fade hint, and the panel padding, none of which TmaTabs'
   generic defaults own. `.mud-list-item`/`.tab-scroll-x`/`.mud-icon-button` selectors stay untouched — the tab
   BODIES (recipe/allergens/…) have not retired their own MudList/MudIconButton controls yet (NEW.4-NEW.7). */
.admin-item-editor,
.admin-item-editor .app-section,
.admin-item-editor .item-editor-tabs,
.admin-item-editor .tab-panel {
    max-width: 100%;
    min-width: 0;
}

/* FB-58: a right-edge fade hints the strip keeps going off-screen — cheap and state-free, costs nothing when the
   tabs already fit. TmaTabs' own `<nav class="tabs item-editor-tabs">` IS the scrolling element (no wrapper div
   to reach through), so the mask applies straight to the page's own class. */
.item-editor-tabs {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}

/* FB-58: a compact "jump to tab" selector beside the strip reaches a facet several swipes away in one tap instead
   of blind scrolling. It stays narrow on a phone and never forces the page wider than the viewport. */
.item-editor-tab-jump {
    margin-bottom: 0.5rem;
    max-width: 12rem;
}

/* This editor's own 48px touch target — a notch above the kit's own 44px `.tab` default (TmaTabs.razor.css) —
   for the denser nine-tab strip specifically. */
.item-editor-tabs .tab {
    min-height: 48px;
}

.admin-item-editor .tab-panel .mud-icon-button {
    min-height: 44px;
    min-width: 44px;
}

/* Wide sub-widgets (the computed-nutrition table) scroll inside their own box rather than pushing the page wide. */
.admin-item-editor .tab-scroll-x {
    max-width: 100%;
    overflow-x: auto;
}

/* Facet-list rows (a name plus presence/exposure chips and icon actions) wrap on a narrow screen instead of forcing the
   page wider than the viewport; letting the name shrink to 0 keeps the whole row inside 375px. */
.admin-item-editor .mud-list-item .d-flex.align-center {
    flex-wrap: wrap;
    min-width: 0;
}

.admin-item-editor .mud-list-item .flex-grow-1 {
    min-width: 0;
}

/* Compact panel padding on phones keeps the tab content inside the narrow viewport. */
.admin-item-editor .tab-panel {
    padding: 0.85rem;
}

@media (min-width: 600px) {
    /* Layer the roomier desktop padding on top once there's width to spare. */
    .admin-item-editor .tab-panel {
        padding: 1.25rem;
    }
}
/* AR.1 item-editor mobile-first — end */

/* UI1.3 order-queue filter pills — start */
/* The order-queue status filter pills (All / Requested / Needs Edit / …) are tapped one-handed mid-service, so every
   pill clears the 44px touch floor (docs/05 §4 — WCAG 2.5.5). min-height, never a fixed height, so the label stays
   centered; the set keeps wrapping at 375px instead of pushing the page into horizontal scroll. Scoped to this filter
   row only — dense status chips elsewhere on the console are read, not tapped, and stay compact. */
.order-queue-filter-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    row-gap: 0.25rem;
}

.order-queue-filter-pills .mud-chip {
    min-height: 44px;
}
/* UI1.3 order-queue filter pills — end */

/* AR.2 admin authoring rows mobile-first — start */
/* Several operator-authoring pages pack a label/switch plus one or more MudBlazor inputs and a button into a single
   inline flex row (location & menu-availability hours, the modifier add-option row, the kitchen-area rows, the
   ticket-routing item overrides). That fits a desktop but overflows a 375px phone — each MudBlazor input carries an
   intrinsic ~180px min-width, so two time pickers, or a field + capped select + button, run past the viewport. These
   rules are authored mobile-first: the row wraps and its children may shrink to 0 so the whole row stays inside the
   narrow viewport. Nothing here forces a desktop-only width, so there is no max-width claw-back; on a wide screen the
   row simply doesn't need to wrap. Scoped to .admin-authoring-row so no other console row is affected. Layout only —
   no behaviour change. */
.admin-authoring-row {
    flex-wrap: wrap;
}

.admin-authoring-row > * {
    min-width: 0;
}

/* The one-time integration credential (client id + secret) is a long unbroken token; let it break so the alert can't
   push the page wider than the viewport (matches the client-list caption, which already breaks anywhere). */
.credential-token {
    overflow-wrap: anywhere;
}
/* AR.2 admin authoring rows mobile-first — end */

/* FB-92 descriptor-review mobile-first — start */
/* The /admin/descriptor-review console is triaged from phones as much as desktops, and each of its five sections
   carries a region wider than a 375px viewport: the provenance tables, the triage/catalog card grids, and the
   filter panels (whose shared desktop template starts at a 16rem column). Authored mobile-first à la AR.1: the
   base rules target 375px — single column, stacked cards, self-contained horizontal scroll for the tables, 44px
   touch targets — and min-width queries layer the desktop grids back on. The admin canvas clips the shell
   (FB-86 containment rule), so every region here must stay inside its own box: max-width: 100% / min-width: 0
   down the chain. Scoped to the descriptor-review-* classes only. Layout only — no decision-flow changes. */
.descriptor-review-section,
.descriptor-review-section .admin-metric-grid,
.descriptor-review-section .admin-workspace-grid,
.descriptor-review-section .admin-workspace-card,
.descriptor-review-section .admin-workspace-card > *,
.descriptor-review-section .tool-access-filter-panel {
    max-width: 100%;
    min-width: 0;
}

/* The wide provenance tables scroll inside their own wrapper instead of pushing the shell wide; the table keeps
   its readable min-width (the FB-40 rule above) and this wrapper owns the overflow. */
.descriptor-review-scroll-x {
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
}

/* Base = 375px: metrics, triage cards, and the filter panels all stack to one full-width column. */
.descriptor-review-metric-grid.admin-metric-grid,
.descriptor-review-card-grid.admin-workspace-grid,
.descriptor-review-filter-panel.tool-access-filter-panel {
    grid-template-columns: 1fr;
}

.descriptor-review-filter-panel .tool-access-filter-summary {
    min-width: 0;
}

/* A long submitted/canonical term breaks instead of forcing its card wider than the viewport. */
.descriptor-review-section .admin-workspace-card .mud-typography-h6 {
    overflow-wrap: anywhere;
}

/* 44px touch floor on the card decision/draft actions; the table decision buttons already carry it (FB-40). */
.descriptor-review-section .descriptor-review-actions .mud-button-root,
.descriptor-review-section .descriptor-draft-panel .mud-button-root {
    min-height: 44px;
}

@media (min-width: 640px) {
    /* Layer the shared multi-column grids back on once there's width to spare. */
    .descriptor-review-metric-grid.admin-metric-grid {
        grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    }

    .descriptor-review-card-grid.admin-workspace-grid {
        grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    }

    .descriptor-review-filter-panel.tool-access-filter-panel {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 1100px) {
    /* The filter panels return to the full desktop search/filters/summary row. */
    .descriptor-review-filter-panel.tool-access-filter-panel {
        grid-template-columns: minmax(16rem, 1.4fr) minmax(12rem, 0.8fr) minmax(12rem, 0.8fr) auto;
    }
}
/* FB-92 descriptor-review mobile-first — end */