/* ============================================================================
   CRBS DESIGN SYSTEM
   ----------------------------------------------------------------------------
   One palette, one type scale, one set of component styles, one stacking order.

   Why this file exists
   --------------------
   Styling had grown page by page: a maroon-and-gold Order section, an indigo
   onboarding flow, purple gradients on checkout, and roughly a dozen ad-hoc
   z-index values between 1 and 100000. Every page looked like a different
   product, and dropdowns inside modals disappeared behind them.

   Everything here is defined once as custom properties and consumed by the
   pages. Retoning the application is now editing the token block below, not
   hunting through 59 components.

   Load order matters: this comes after bootstrap.min.css and app.css so it
   wins, and before the page-specific sheets that still need to override it.

   Sections
   --------
     1. Tokens (colour, type, space, radius, shadow, z-index)
     2. Base elements
     3. Buttons
     4. Forms
     5. Cards and panels
     6. Tables
     7. Badges and status
     8. Modals and overlays
     9. Radzen overlay components  <-- the dropdown-behind-modal fix
    10. Legacy token bridges
    11. Responsive
   ========================================================================== */


/* ============================================================================
   1. TOKENS
   ========================================================================== */

:root {
    /* ---- Brand ----------------------------------------------------------
       Every colour below derives from the --auth-* palette in
       auth-theme.css, which is what the sign-in and registration screens
       use. One edit there re-skins the sign-in screens AND the whole signed-
       in application, so the product reads as one thing rather than as a
       marketing page bolted to an admin tool.

       There is deliberately no pure black anywhere: --auth-ink is a warm
       near-black and --auth-shade is a deep warm brown, so shadows, hovers
       and borders stay in the same family as the brand instead of going
       grey. */

    /* ---- Text -----------------------------------------------------------
       Colour is reserved for meaning, not decoration. */
    --c-ink:            var(--auth-ink);        /* headings, emphasis */
    --c-ink-2:          var(--auth-ink-soft);   /* body text */
    --c-ink-3:          color-mix(in srgb, var(--auth-ink) 55%, var(--auth-canvas)); /* muted, helper copy */
    --c-ink-4:          var(--auth-ink-faint);  /* placeholders, disabled */

    /* ---- Lines ----------------------------------------------------------
       Warm hairlines, mixed from the accent, matching the sign-in card. */
    --c-line:           var(--auth-line);
    --c-line-strong:    var(--auth-line-hover);
    --c-line-soft:      var(--auth-line-soft);

    /* ---- Surfaces -------------------------------------------------------
       The warm off-white canvas is what makes the app feel like the sign-in
       page rather than a generic grey dashboard. */
    --c-surface:        var(--auth-surface);
    --c-surface-2:      color-mix(in srgb, var(--auth-accent) 5%, var(--auth-surface));
    --c-bg:             var(--auth-canvas);

    /* ---- Primary --------------------------------------------------------
       The action colour, straight from the sign-in button. Primary buttons
       used to be near-black; they are now brand. */
    --c-primary:        var(--auth-primary);
    --c-primary-hover:  var(--auth-primary-strong);
    --c-primary-soft:   var(--auth-primary-soft);
    --c-on-primary:     var(--auth-on-primary);

    /* ---- Accent ---------------------------------------------------------
       The warm gold. Decorative and for selection; never for body text at
       small sizes - use --c-accent-ink where it has to be read. */
    --c-accent:         var(--auth-primary);
    --c-accent-hover:   var(--auth-primary-strong);
    --c-accent-soft:    var(--auth-primary-soft);
    --c-accent-line:    var(--auth-accent-line);
    --c-accent-ink:     var(--auth-accent-ink);
    --c-ring:           var(--auth-ring);

    /* ---- Semantic -------------------------------------------------------

       TWO HUES ONLY: the brand rose and the brand gold. There is no green,
       no blue, no purple and no orange in this application.

       These four used to be a conventional traffic-light set - a green
       #1f7a4d, a blue #1f6a8c, an orange, a purple - which is what made the
       product look like several products at once. They are now four steps
       inside the brand:

         success   gold, deep enough to carry white text
         warning   the brand rose
         danger    a deeper red than the rose, so an error still outranks it
         info      a quiet, desaturated rose that recedes

       They stay distinguishable by DEPTH rather than by hue, and every one of
       them is used with an icon and a word beside it - a badge that relies on
       colour alone to say "paid" versus "overdue" was never readable to the
       ~8% of men who cannot separate those two hues anyway.

       Change them HERE. Every badge, button, alert and chip in the product
       derives from these; a page that hard-codes a hex is a page that will
       drift, and section 14 lists the ones that had to be brought back.

       Contrast was checked for the solid treatments, which put #fff on the
       base colour: success lands near 5:1 and danger near 7:1 against white,
       both past AA for body text. If you darken or lighten any of these,
       re-check - 56% ink on white failed AA twice in this file's history. */

    /* Positive, complete, paid, in stock. Gold, not green. */
    --c-success:        var(--auth-accent-ink);
    --c-success-soft:   var(--auth-accent-soft);
    --c-success-line:   var(--auth-accent-line);

    /* Needs attention, but nothing has gone wrong yet. */
    --c-warning:        var(--auth-primary);
    --c-warning-soft:   var(--auth-primary-soft);
    --c-warning-line:   color-mix(in srgb, var(--auth-primary) 34%, var(--auth-surface));

    /* Something IS wrong. Deeper than the rose so it still reads as more
       serious when the two sit next to each other. */
    --c-danger:         var(--auth-danger);
    --c-danger-soft:    color-mix(in srgb, var(--auth-danger) 9%, var(--auth-surface));
    --c-danger-line:    var(--auth-danger-line);

    /* Neutral, informational. Meant to recede - it is the one of the four
       that should never be the loudest thing on a card. */
    --c-info:           color-mix(in srgb, var(--auth-primary) 62%, var(--auth-ink));
    --c-info-soft:      color-mix(in srgb, var(--auth-primary) 7%, var(--auth-surface));
    --c-info-line:      color-mix(in srgb, var(--auth-primary) 22%, var(--auth-surface));

    /* ---- Typography -----------------------------------------------------
       A 14px base. This is a dense business application, so it is not the
       16px a marketing site would use - but 13px was too small to read
       comfortably for a full shift, and it was worse than it looked: the
       setup pages are built almost entirely from Bootstrap's "-sm" control
       variants, which sat *below* the base and put real labels at 12px and
       helper text at 10.7px.

       Everything is sized off this scale. Do not type a px value into a page:
       a page that opts out is the page that ends up looking different.

       'Inter' used to head the stack and was never loaded - no @font-face,
       no <link>. It silently fell through to Segoe UI on Windows, which is
       what the application has always actually rendered in. Worse, it meant a
       machine that happened to have Inter installed rendered differently from
       every other machine. The stack now names what is really being used. To
       adopt a webfont, load it and put it at the front here - one edit, whole
       application. */
    --f-sans: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, Roboto,
              'Helvetica Neue', Arial, sans-serif;
    --f-mono: 'Cascadia Mono', 'SF Mono', Consolas, monospace;

    /* Nothing in the application may render below --fs-xs. */
    --fs-xs:   12px;
    --fs-sm:   13px;
    --fs-base: 14px;
    --fs-md:   15px;
    --fs-lg:   17px;
    --fs-xl:   20px;
    --fs-2xl:  24px;

    --fw-normal:   400;
    --fw-medium:   500;
    --fw-semibold: 600;
    /* The page title is the only thing in the application that goes above
       semibold. Declared so `var(--fw-bold)` resolves - it was used without
       being defined, which silently left page titles at the inherited 400. */
    --fw-bold:     700;

    --lh-tight: 1.25;
    --lh-base:  1.5;

    /* ---- Space ----------------------------------------------------------
       A 4px rhythm. Spacing that is picked off this scale looks intentional;
       spacing typed as "13px" never does. */
    --s-1:  4px;
    --s-2:  8px;
    --s-3:  12px;
    --s-4:  16px;
    --s-5:  20px;
    --s-6:  24px;
    --s-8:  32px;
    --s-10: 40px;

    /* ---- Radius ---------------------------------------------------------
       Restrained. Large radii read as consumer software, not business
       software. */
    --r-sm: 4px;
    --r-md: 6px;
    --r-lg: 8px;
    --r-full: 999px;

    /* ---- Elevation ------------------------------------------------------
       Three levels, all soft. Heavy drop shadows were a large part of why
       the old screens felt cluttered.

       Mixed from --auth-shade, the deep warm brown, rather than from black -
       a grey shadow over a warm canvas reads as dirt. */
    --sh-sm: 0 1px 2px color-mix(in srgb, var(--auth-shade) 7%, transparent);
    --sh-md: 0 2px 8px color-mix(in srgb, var(--auth-shade) 10%, transparent);
    --sh-lg: 0 8px 28px color-mix(in srgb, var(--auth-shade) 16%, transparent);

    --transition: 140ms ease;

    /* ---- Stacking order -------------------------------------------------
       THE ONE PLACE z-index IS DECIDED.

       Pages previously invented their own values (1, 2, 5, 100, 200, 999,
       1000, 1100, 1200, 9998, 9999, 10000, 99999, 100000), so whether a
       dropdown appeared above a modal came down to which number somebody
       happened to type. Use these instead of a literal.

       The gap between each level leaves room for a component that genuinely
       needs to sit between two of them. */
    --z-base:      1;
    --z-sticky:    100;   /* sticky table headers, toolbars */
    --z-nav:       1000;  /* site navigation */
    --z-nav-menu:  1100;  /* open navigation dropdowns */
    --z-backdrop:  1900;  /* modal scrim */
    --z-modal:     2000;  /* modal dialogs */
    --z-popup:     3000;  /* dropdowns, date pickers, autocomplete panels */
    --z-tooltip:   4000;  /* tooltips, popovers */
    --z-toast:     5000;  /* toastr notifications */
    --z-loader:    6000;  /* full-screen blocking loader */

    /* ---- Layout ---------------------------------------------------------
       The chrome is sticky at the top of every signed-in screen, and it is two
       rows: the header (brand, trail, search, profile) and the menu bar
       underneath it. Anything else that sticks has to clear BOTH, or it slides
       underneath and the user loses the very thing that was meant to stay
       visible. Declared here so no page has to guess the numbers. */
    /* The header row was 60px - taller than anything in it actually needs
       (every control in it is a 36px pill or a 34px brand mark), so it was
       carrying dead space above the menu that mattered more. It and the menu
       row were then both shrunk further still, to give the content area back
       as much of the screen as the chrome can spare without controls starting
       to crowd each other - a restaurant reading a table of orders cares about
       that vertical space more than the chrome does. */
    --app-bar-h: 44px;    /* the header row */
    /* The menu row was then given a few pixels BACK - at 38px the pills were
       sitting inside 30px of height with 4px above and below, which read as
       cramped against the header row over it. 44px is the same bar with room
       around the pills; the header row above it is deliberately unchanged. */
    --app-menu-h: 44px;   /* one line of the menu bar, hairlines included */

    /* What every sticky toolbar and table header in the product offsets
       itself by. The two rows above are the normal case; the menu bar is
       allowed to WRAP when a restaurant has more menus than fit on a line,
       rather than hiding any of them, so the shell measures the chrome after
       every layout and overrides this with its real height (see menu.js).
       The calc is what holds until that first measurement lands. */
    --app-header-h: calc(var(--app-bar-h) + var(--app-menu-h));
}


/* ============================================================================
   2. BASE
   ========================================================================== */

html, body {
    font-family: var(--f-sans);
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    color: var(--c-ink-2);
    background-color: var(--c-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
    color: var(--c-ink);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    margin-top: 0;
}

h1 { font-size: var(--fs-2xl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }
/* h5 was the same size as body text, which is why a page header like
   "Location Setup" did not read as a heading at all. Most setup pages use h5
   for their title and h6 for a card title, so those two need to be a step
   apart from the text around them. */
h5 { font-size: var(--fs-lg); }
h6 { font-size: var(--fs-md); }

/* Bootstrap sizes these as 0.875em, which compounds: a <small> inside a
   control that is already below the base ends up near 10px. Pin them to the
   scale instead so "small" means one step down, wherever it appears. */
small, .small { font-size: var(--fs-sm); }

a {
    color: var(--c-accent);
    text-decoration: none;
    transition: color var(--transition);
}

a:hover { color: var(--c-accent-hover); text-decoration: underline; }

hr { border-color: var(--c-line); opacity: 1; }

/* One focus treatment everywhere. The old rule painted a white ring plus a
   bright blue ring on every control, which flashed on each click. */
:focus-visible,
.form-control:focus,
.form-select:focus,
.btn:focus-visible {
    outline: none;
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px var(--c-accent-soft);
}

.btn:focus,
.btn:active:focus,
.btn-link.nav-link:focus,
.form-control:focus,
.form-check-input:focus {
    box-shadow: 0 0 0 3px var(--c-accent-soft);
}

::selection { background: var(--c-accent-soft); color: var(--c-ink); }

/* Scrollbars: thin and neutral, so panels with overflow stop shouting. */
* { scrollbar-width: thin; scrollbar-color: var(--c-line-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: var(--c-line-strong);
    border-radius: var(--r-full);
    border: 3px solid transparent;
    background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background-color: var(--c-ink-4); background-clip: content-box; }

.text-muted { color: var(--c-ink-3) !important; }


/* ============================================================================
   3. BUTTONS
   ----------------------------------------------------------------------------
   Primary is ink, not colour. When every button is blue or green nothing
   reads as the main action; with a neutral primary, the single coloured
   button on a screen carries real weight.
   ========================================================================== */

.btn {
    font-family: var(--f-sans);
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    line-height: 1.4;
    padding: 7px 14px;
    border-radius: var(--r-md);
    border: 1px solid transparent;
    transition: background-color var(--transition), border-color var(--transition), color var(--transition);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    white-space: nowrap;
}

.btn:active { transform: none; }
.btn i, .btn .fa, .btn .fas, .btn .far { font-size: 0.95em; }

/* "-sm" is not an exception in this application - it is the default. 82 of the
   buttons across 32 pages are btn-sm, and nearly every setup form is built
   from form-control-sm. Sizing them a step below the base therefore shrank the
   whole application rather than one emphatic control. They keep the tighter
   padding, which is what actually makes a dense form work, but read at the
   same size as the text beside them. */
.btn-sm { padding: 5px 10px; font-size: var(--fs-base); border-radius: var(--r-sm); }
.btn-lg { padding: 10px 20px; font-size: var(--fs-md); }

/* Primary is the brand colour from the sign-in button. .btn-dark is folded in
   deliberately: pages used it as "the important one", and leaving it black
   would put two competing primary treatments on the same screen. */
.btn-primary,
.btn-dark {
    background-color: var(--c-primary);
    border-color: var(--c-primary);
    color: var(--c-on-primary);
}
.btn-primary:hover, .btn-dark:hover,
.btn-primary:focus, .btn-dark:focus,
.btn-primary:active, .btn-dark:active {
    background-color: var(--c-primary-hover);
    border-color: var(--c-primary-hover);
    color: var(--c-on-primary);
}
.btn-primary:disabled, .btn-dark:disabled {
    background-color: color-mix(in srgb, var(--c-primary) 40%, var(--auth-surface));
    border-color: color-mix(in srgb, var(--c-primary) 40%, var(--auth-surface));
}

.btn-outline-dark {
    background: transparent;
    border-color: var(--c-line-strong);
    color: var(--c-ink-2);
}
.btn-outline-dark:hover, .btn-outline-dark:focus, .btn-outline-dark:active {
    background: var(--c-primary-soft);
    border-color: var(--c-primary);
    color: var(--c-primary);
}

.btn-secondary,
.btn-light,
.btn-outline-secondary {
    background-color: var(--c-surface);
    border-color: var(--c-line-strong);
    color: var(--c-ink-2);
}
.btn-secondary:hover, .btn-light:hover, .btn-outline-secondary:hover,
.btn-secondary:focus, .btn-light:focus, .btn-outline-secondary:focus,
.btn-secondary:active, .btn-light:active, .btn-outline-secondary:active {
    background-color: var(--c-surface-2);
    border-color: var(--c-ink-4);
    color: var(--c-ink);
}

.btn-success {
    background-color: var(--c-success);
    border-color: var(--c-success);
    color: #fff;
}
.btn-success:hover, .btn-success:focus, .btn-success:active {
    /* Darkened toward --auth-shade, which is what every hover in this file
       darkens towards - it used to be a darker green. */
    background-color: color-mix(in srgb, var(--c-success) 82%, var(--auth-shade));
    border-color: color-mix(in srgb, var(--c-success) 82%, var(--auth-shade));
    color: #fff;
}

.btn-danger {
    background-color: var(--c-danger);
    border-color: var(--c-danger);
    color: #fff;
}
.btn-danger:hover, .btn-danger:focus, .btn-danger:active {
    background-color: #8f1e18; border-color: #8f1e18; color: #fff;
}

.btn-warning {
    background-color: var(--c-warning);
    border-color: var(--c-warning);
    color: #fff;
}
.btn-warning:hover, .btn-warning:focus, .btn-warning:active {
    background-color: #8a560e; border-color: #8a560e; color: #fff;
}

.btn-info {
    background-color: var(--c-info);
    border-color: var(--c-info);
    color: #fff;
}
.btn-info:hover, .btn-info:focus, .btn-info:active {
    background-color: color-mix(in srgb, var(--c-info) 82%, var(--auth-shade));
    border-color: color-mix(in srgb, var(--c-info) 82%, var(--auth-shade));
    color: #fff;
}

.btn-outline-primary {
    background: transparent; border-color: var(--c-line-strong); color: var(--c-primary);
}
.btn-outline-primary:hover, .btn-outline-primary:focus, .btn-outline-primary:active {
    background: var(--c-primary-soft); border-color: var(--c-primary); color: var(--c-primary);
}

.btn-outline-danger {
    background: transparent; border-color: var(--c-danger-line); color: var(--c-danger);
}
.btn-outline-danger:hover, .btn-outline-danger:focus, .btn-outline-danger:active {
    background: var(--c-danger-soft); border-color: var(--c-danger); color: var(--c-danger);
}

.btn-outline-success {
    background: transparent; border-color: var(--c-success-line); color: var(--c-success);
}
.btn-outline-success:hover, .btn-outline-success:focus, .btn-outline-success:active {
    background: var(--c-success-soft); border-color: var(--c-success); color: var(--c-success);
}

.btn-link { color: var(--c-accent); padding-left: 0; padding-right: 0; }


/* ============================================================================
   4. FORMS
   ========================================================================== */

.form-label,
label.form-label {
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--c-ink-2);
    margin-bottom: var(--s-1);
}

.form-control,
.form-select,
input[type="text"].form-control,
input[type="number"].form-control,
input[type="email"].form-control,
input[type="password"].form-control,
input[type="date"].form-control,
textarea.form-control {
    font-size: var(--fs-base);
    color: var(--c-ink);
    background-color: var(--c-surface);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    padding: 7px 10px;
    transition: border-color var(--transition), box-shadow var(--transition);
    box-shadow: none;
}

.form-control::placeholder { color: var(--c-ink-4); }

.form-control:disabled,
.form-control[readonly],
.form-select:disabled {
    background-color: var(--c-surface-2);
    color: var(--c-ink-3);
    cursor: not-allowed;
}

/* Same reasoning as .btn-sm above: tighter box, same readable text. A value
   the user has typed is the last thing that should be hard to read. */
.form-control-sm, .form-select-sm { padding: 5px 8px; font-size: var(--fs-base); }

.form-text {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
    margin-top: var(--s-1);
}

/* Validation. The old rules drew a raw 1px red/green outline that sat outside
   the border and misaligned the control. */
.valid.modified:not([type=checkbox]) {
    outline: none;
    border-color: var(--c-success);
}

.invalid,
.form-control.invalid {
    outline: none;
    border-color: var(--c-danger);
}

.validation-message,
.text-danger {
    color: var(--c-danger) !important;
    font-size: var(--fs-xs);
}

.form-check-input { border-color: var(--c-line-strong); }
.form-check-input:checked {
    background-color: var(--c-primary);
    border-color: var(--c-primary);
}
.form-check-label { font-size: var(--fs-base); color: var(--c-ink-2); }

.input-group-text {
    background-color: var(--c-surface-2);
    border-color: var(--c-line-strong);
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
}


/* ============================================================================
   5. CARDS AND PANELS
   ========================================================================== */

.card {
    background-color: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-sm);
}

.card-header {
    background-color: var(--c-surface);
    border-bottom: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold) !important;
    color: var(--c-ink);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
}

.card-body { padding: var(--s-4); }

.card-footer {
    background-color: var(--c-surface-2);
    border-top: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
}

.card-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--c-ink); }

/* Nested cards should not stack borders and shadows into a ridge. */
.card .card { box-shadow: none; }

.bg-light { background-color: var(--c-surface-2) !important; }


/* ============================================================================
   6. TABLES
   ----------------------------------------------------------------------------
   Horizontal rules only. Vertical grid lines make a table look like a
   spreadsheet and add visual noise without aiding scanning.
   ========================================================================== */

.table {
    font-size: var(--fs-base);
    color: var(--c-ink-2);
    border-color: var(--c-line);
    margin-bottom: 0;
}

.table > thead > tr > th {
    background-color: var(--c-surface-2);
    color: var(--c-ink-3);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 1px solid var(--c-line-strong);
    padding: var(--s-2) var(--s-3);
    white-space: nowrap;
}

.table > tbody > tr > td {
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--c-line-soft);
    vertical-align: middle;
}

.table > tbody > tr:last-child > td { border-bottom: none; }

.table-hover > tbody > tr:hover > td { background-color: var(--c-surface-2); }

.table-striped > tbody > tr:nth-of-type(odd) > td { background-color: var(--c-surface-2); }

.table-sm > thead > tr > th,
.table-sm > tbody > tr > td { padding: 5px var(--s-2); }

/* Numbers line up only in a tabular font. */
.table td.text-end,
.table th.text-end,
.num { font-variant-numeric: tabular-nums; }

/* Any wide table scrolls inside its own container rather than pushing the
   page sideways. */
.table-responsive { border-radius: var(--r-md); }


/* ============================================================================
   4b. APP SELECT
   ----------------------------------------------------------------------------
   Styling for Components/Common/Pages/AppSelect.razor, the application's own
   select control.

   The panel is a child of the field and absolutely positioned against it, so
   it inherits the field's stacking context and cannot render behind the
   dialog it sits in. --z-popup is applied only so it clears its own siblings
   inside a form; it is not fighting a global stacking order the way a
   body-mounted panel has to.

   Every value comes from the tokens, so the control matches the inputs
   beside it without anyone re-describing what an input looks like.
   ========================================================================== */

.app-select {
    position: relative;
    display: block;
    width: 100%;
}

.app-select__trigger {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    width: 100%;
    min-height: 34px;
    padding: 6px 10px;
    background: var(--c-surface);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    font-family: var(--f-sans);
    font-size: var(--fs-base);
    color: var(--c-ink);
    text-align: left;
    cursor: pointer;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.app-select__trigger:hover:not(:disabled) { border-color: var(--c-ink-4); }

.app-select__trigger:focus-visible,
.app-select__trigger.is-open {
    outline: none;
    border-color: var(--c-primary);
    box-shadow: 0 0 0 3px var(--c-primary-soft);
}

.app-select.is-disabled .app-select__trigger,
.app-select__trigger:disabled {
    background: var(--c-surface-2);
    color: var(--c-ink-3);
    cursor: not-allowed;
}

/* The value takes the space; the icons keep their own. Truncating here is
   what stops a long name pushing the chevron off the control. */
.app-select__value {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-select__value.is-placeholder { color: var(--c-ink-4); }

.app-select__arrow {
    flex: 0 0 auto;
    color: var(--c-ink-3);
    font-size: 0.8em;
    transition: transform var(--transition);
}

.app-select__trigger.is-open .app-select__arrow { transform: rotate(180deg); }

.app-select__clear {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: var(--r-full);
    color: var(--c-ink-3);
    font-size: 0.75em;
    cursor: pointer;
}
.app-select__clear:hover { background: var(--c-line-soft); color: var(--c-ink); }

/* ---- Panel ------------------------------------------------------------- */

.app-select__panel {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: var(--z-popup);
    background: var(--c-surface);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    box-shadow: var(--sh-lg);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    max-height: 280px;
}

/* Opens upward when there is not enough room below. */
.app-select__panel.is-above {
    top: auto;
    bottom: calc(100% + 4px);
}

.app-select__filter {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--c-line);
    color: var(--c-ink-3);
    flex: 0 0 auto;
}

.app-select__filter i { font-size: 0.85em; }

.app-select__filter input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    font-family: var(--f-sans);
    font-size: var(--fs-base);
    color: var(--c-ink);
}

.app-select__filter input::placeholder { color: var(--c-ink-4); }

.app-select__options {
    overflow-y: auto;
    padding: var(--s-1);
    flex: 1 1 auto;
}

.app-select__option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    width: 100%;
    padding: 7px 10px;
    border: none;
    background: transparent;
    border-radius: var(--r-sm);
    font-family: var(--f-sans);
    font-size: var(--fs-base);
    color: var(--c-ink-2);
    text-align: left;
    cursor: pointer;
}

.app-select__option-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-select__option:hover,
.app-select__option.is-active { background: var(--c-surface-2); color: var(--c-ink); }

.app-select__option.is-selected {
    background: var(--c-primary-soft);
    color: var(--c-primary);
    font-weight: var(--fw-medium);
}

.app-select__option i { flex: 0 0 auto; font-size: 0.8em; }

.app-select__empty {
    padding: var(--s-3);
    text-align: center;
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
}


/* ============================================================================
   4c. APP MULTI SELECT
   ----------------------------------------------------------------------------
   Components/Common/Pages/AppMultiSelect.razor - AppSelect's sibling for the
   case where the answer is a SET rather than one value.

   An inline list rather than a panel, on purpose: a multi-select hidden behind
   a closed control has to teach the reader twice, that it opens and that it
   takes more than one. The ticks are the whole point, so they are on screen.
   Same tokens, same hairline, same focus ring as the single select, so a form
   carrying both reads as one form.
   ========================================================================== */

.app-ms {
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    background: var(--c-surface);
    overflow: hidden;
}

.app-ms__bar {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2);
    border-bottom: 1px solid var(--c-line);
    background: var(--c-surface-2);
    flex-wrap: wrap;
}

.app-ms__search {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    flex: 1 1 130px;
    min-width: 0;
    padding: 3px 8px;
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-sm);
    background: var(--c-surface);
    color: var(--c-ink-3);
}

.app-ms__search i { font-size: 0.8em; flex: none; }

.app-ms__search input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    font-family: var(--f-sans);
    font-size: var(--fs-sm);
    color: var(--c-ink);
}

.app-ms__search input::-webkit-search-cancel-button { display: none; }

/* The count is the feedback that makes a multi-select legible: it is the one
   thing that stays true when the search has hidden most of the ticks. */
.app-ms__count {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: var(--c-ink-3);
    white-space: nowrap;
    margin-left: auto;
}

.app-ms__act {
    flex: none;
    padding: 2px 8px;
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-sm);
    background: var(--c-surface);
    color: var(--c-ink-2);
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    cursor: pointer;
    white-space: nowrap;
}

.app-ms__act:hover:not(:disabled) { border-color: var(--c-primary); color: var(--c-primary); }
.app-ms__act:disabled { opacity: .45; cursor: not-allowed; }

/* Bounded, so a four-hundred-item menu scrolls inside the field instead of
   pushing the form it sits in off the bottom of the screen. */
.app-ms__list {
    max-height: 210px;
    overflow-y: auto;
    padding: var(--s-1);
}

.app-ms__opt {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    width: 100%;
    margin: 0;
    padding: 5px 8px;
    border-radius: var(--r-sm);
    font-size: var(--fs-sm);
    color: var(--c-ink-2);
    cursor: pointer;
}

.app-ms__opt:hover { background: var(--c-surface-2); color: var(--c-ink); }

.app-ms__opt.is-on {
    background: var(--c-primary-soft);
    color: var(--c-primary);
    font-weight: var(--fw-medium);
}

/* A real, visible box. Bootstrap's default hairline on a white row read as a
   bullet rather than as something to tick, which is half of what "you can
   choose several" has to communicate. */
.app-ms__opt input {
    flex: none;
    width: 16px;
    height: 16px;
    margin: 0;
    border: 1.5px solid var(--c-ink-3, #8a7f75);
    cursor: pointer;
}

.app-ms__opt input:checked {
    background-color: var(--c-primary);
    border-color: var(--c-primary);
}

.app-ms__opt input:focus { box-shadow: var(--focus-ring, 0 0 0 3px var(--c-primary-soft)); }

/* Never truncated: the whole reason to tick one of these is that you read it. */
.app-ms__opt-text { min-width: 0; overflow-wrap: anywhere; }

.app-ms__empty {
    margin: 0;
    padding: var(--s-4) var(--s-3);
    text-align: center;
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
}


/* ============================================================================
   6a2. PAGE HEADER
   ----------------------------------------------------------------------------
   The one page header. Rendered by Components/Common/Pages/PageHeader.razor;
   do not hand-roll another.

   There were three before this existed, and that is what made the application
   look like several products:

     * `.card-header` with a single word in it - eighteen setup screens
     * `.stock-page__head` with a title, an explanatory subtitle and actions -
       eleven screens (Happy Hour, Quick Setup, Loyalty, Business Hours,
       Table Reservations, Customer Feedback, the pricing report, the stock
       dashboard, the default catalogue)
     * `.list-page-header`, which was declared in ONE page's <style> block and
       then copied to a second page that had no such block - so that page's
       header rendered with no styling at all

   This takes the best of the three: the title and subtitle from the stock
   family, because "what is this screen for" is worth a line of prose, and a
   right-hand action slot. It is defined here rather than in stock.css so it
   belongs to the application rather than to one module.

   A screen that only needs a word still uses it - with no Subtitle, it is
   simply a title, and it is still the same type, weight and spacing as every
   other page. That consistency is the whole point.

   The `.stock-page__*` names this replaced are gone: they were defined in
   stock.css, which loads after this file and therefore decided what a page
   header looked like for twelve screens. All twelve now render <PageHeader>,
   so there is one definition of a page header and no alias to keep in step.
   ========================================================================== */

.page-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--s-4);
    flex-wrap: wrap;
    margin-bottom: var(--s-4);
    padding-bottom: var(--s-3);
    border-bottom: 1px solid var(--c-line);
}

.page-head__text { min-width: 0; }

/* An edit form reached FROM a list keeps a way back to that list, to the left
   of the title. The browser's own back button does the same thing, but the two
   form pages had each grown their own square chip instead of borrowing it, so
   it lives here now - same header, one more optional slot. */
.page-head__lead {
    display: flex;
    align-items: flex-start;
    gap: var(--s-3);
    min-width: 0;
    flex: 1 1 auto;
}

.page-head__back {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    padding: 0;
    margin-top: 2px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--c-line);
    border-radius: 8px;
    background: var(--c-surface);
    color: var(--c-ink-3);
    cursor: pointer;
    transition: background-color .15s, color .15s, border-color .15s;
}

.page-head__back:hover,
.page-head__back:focus-visible {
    background: var(--c-primary-soft);
    border-color: var(--c-primary);
    color: var(--c-primary);
}

.page-head__title {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
    letter-spacing: -.01em;
    color: var(--c-ink);
}

.page-head__title i {
    color: var(--c-ink-4);
    font-size: 0.88em;
    margin-right: var(--s-2);
}

/* Prose, so it wraps and stays readable rather than being truncated. */
.page-head__subtitle {
    margin: var(--s-1) 0 0;
    max-width: 78ch;
    color: var(--c-ink-3);
    font-size: var(--fs-base);
    line-height: 1.5;
}

.page-head__actions {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    flex-wrap: wrap;
}

/* The actions drop under the title on a narrow screen rather than squeezing
   it - the never-truncate rule the app header follows too. */
@media (max-width: 768px) {
    .page-head { flex-direction: column; align-items: stretch; }
    .page-head__actions { justify-content: flex-start; }
}


/* ---- 6a3. Centred single-column page ----------------------------------
   A screen whose whole content is one short form - change password, and
   anything else that is a handful of fields and two buttons.

   These were written as a bare `<div class="card" style="max-width:560px">`,
   which caps the width but says nothing about where the capped box goes, so
   it pinned itself to the left edge of a 1900px window with the page header
   stretched across the full width above it. The form read as having fallen
   off the side of the screen.

   The wrapper centres the column and the header with it, so the title sits
   over the card it belongs to rather than over the empty space beside it.
   Width is capped in ch rather than px so it tracks the type scale, and the
   gutter keeps it off the edge on a phone.
   --------------------------------------------------------------------- */

/* .main has no side padding of its own, so the column carries its gutter:
   without it the title and the card sat flush against both edges of a phone
   (and of any window narrower than the cap). The gutter is added on top of
   the cap, so the form is still 44rem wide on a desktop. */
.page-narrow {
    box-sizing: border-box;
    width: 100%;
    max-width: calc(44rem + 2 * var(--s-4));   /* ~560px of form at a 16px root, the width these forms were already capped at */
    margin-inline: auto;
    padding-inline: var(--s-4);
}

/* The header is part of the column, so it is centred with it - and its own
   text is left-aligned, because a centred label over a left-aligned field is
   worse than either. */
.page-narrow > .page-head { margin-bottom: var(--s-4); }

/* Nothing inside may re-introduce its own width cap; the column owns it. */
.page-narrow > .card { width: 100%; }

@media (max-width: 575.98px) {
    .page-narrow { max-width: 100%; }
}


/* ---- Empty state ------------------------------------------------------
   What a list shows when it has nothing in it.

   Promoted here from one page's <style> block, where a second page had
   already started referencing it and getting nothing. An empty list is the
   first thing a brand-new restaurant sees on most of these screens, and
   "No records to display" tells them neither whether something is broken nor
   what to do next - so the shape is icon, what is empty, why it matters, and
   the one or two ways in.
   --------------------------------------------------------------------- */

.list-empty {
    text-align: center;
    padding: var(--s-10) var(--s-5);
    max-width: 460px;
    margin: 0 auto;
}

.list-empty__icon {
    width: 52px;
    height: 52px;
    margin: 0 auto var(--s-4);
    border-radius: 50%;
    background: var(--c-primary-soft);
    color: var(--c-primary);
    display: grid;
    place-items: center;
    font-size: var(--fs-xl);
}

.list-empty__title {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    margin: 0 0 var(--s-2);
}

.list-empty__text {
    font-size: var(--fs-base);
    line-height: 1.55;
    color: var(--c-ink-2);
    margin: 0 0 var(--s-5);
}

.list-empty__actions {
    display: flex;
    gap: var(--s-2);
    justify-content: center;
    flex-wrap: wrap;
    margin-bottom: var(--s-5);
}

.list-empty__hint { font-size: var(--fs-sm); color: var(--c-ink-3); margin: 0; }
.list-empty__hint a { color: var(--c-primary); font-weight: var(--fw-medium); }

/* A one-line state - "Loading", "Nothing matches that search" - where the
   full empty state would be too much furniture. */
.list-state {
    padding: var(--s-8) var(--s-4);
    text-align: center;
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
}
.list-state--start { text-align: start; max-width: 60ch; }


/* ============================================================================
   6b. LIST VIEWS - CARD AND GRID
   ----------------------------------------------------------------------------
   Roughly twenty setup screens render their list as the same shape: a
   Bootstrap row of column divs, each holding a .setup-box card made of a
   heading, a run of label/value paragraphs, and a row of actions.

   Because the markup is shared, both layouts can be done here in CSS and a
   page only has to say which one is active. Card view is the default and
   needs no class; .data-list--grid re-flows exactly the same markup into
   dense full-width rows. Nothing is duplicated and no page re-implements a
   table.

   The grid view is not a real <table> on purpose: keeping one DOM for both
   views means switching is instant, selection and edit state survive the
   switch, and there is only one thing to keep correct.
   ========================================================================== */

.data-list {
    --card-min: 210px;
}

/* ---- Card view (default) ----------------------------------------------- */

/* Overrides the col-* widths the pages hard-code, so every list gets the
   same rhythm regardless of which column class it happened to be written
   with, and cards size to their content rather than to a 12-column guess. */
.data-list.row {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr));
    gap: var(--s-3);
    margin: 0;
}

.data-list.row > [class*="col-"] {
    width: 100%;
    max-width: none;
    flex: none;
    padding: 0;
}

.data-list .setup-box,
.data-list .location-box {
    height: 100%;
    margin: 0;
    background: var(--c-surface);
    border: 1px solid var(--c-line) !important;
    border-radius: var(--r-lg);
    box-shadow: none;
    padding: var(--s-3) !important;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.data-list .setup-box:hover,
.data-list .location-box:hover {
    transform: none;                       /* the old 2px lift made lists twitch */
    border-color: var(--c-line-strong) !important;
    box-shadow: var(--sh-sm) !important;
}

.data-list .setup-box h6,
.data-list .location-box h6 {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    border-bottom: 1px solid var(--c-line-soft);
    padding-bottom: var(--s-2);
    margin-bottom: var(--s-2) !important;
    display: flex;
    align-items: center;
    gap: var(--s-2);
}

.data-list .setup-box h6 i,
.data-list .location-box h6 i { color: var(--c-ink-4); font-size: 0.9em; }

.data-list .setup-box p { margin-bottom: var(--s-1) !important; line-height: 1.45; }

.data-list .setup-box-label,
.data-list .location-box .label {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--c-ink-3);
    text-transform: none;
    letter-spacing: 0;
}

.data-list .setup-box-value,
.data-list .location-box .value {
    font-size: var(--fs-sm);
    color: var(--c-ink);
    font-weight: var(--fw-normal);
}

/* Actions sit on a divider at the foot of the card, so cards of differing
   height still line their buttons up. */
.data-list .setup-box > .d-flex:last-child,
.data-list .location-box > .d-flex:last-child {
    margin-top: auto !important;
    padding-top: var(--s-2);
    border-top: 1px solid var(--c-line-soft);
    gap: var(--s-2);
}

/* ---- Grid view --------------------------------------------------------- */

.data-list--grid.row {
    grid-template-columns: 1fr;
    gap: 0;
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    overflow: hidden;
    background: var(--c-surface);
}

.data-list--grid .setup-box,
.data-list--grid .location-box {
    border: none !important;
    border-bottom: 1px solid var(--c-line-soft) !important;
    border-radius: 0;
    box-shadow: none !important;
    padding: var(--s-2) var(--s-3) !important;
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-3);
}

.data-list--grid.row > [class*="col-"]:last-child .setup-box,
.data-list--grid.row > [class*="col-"]:last-child .location-box {
    border-bottom: none !important;
}

.data-list--grid .setup-box:hover,
.data-list--grid .location-box:hover {
    background: var(--c-surface-2);
    box-shadow: none !important;
}

/* The name becomes the first column and stops being a heading block. */
.data-list--grid .setup-box h6,
.data-list--grid .location-box h6 {
    border-bottom: none;
    padding-bottom: 0;
    margin-bottom: 0 !important;
    flex: 0 0 210px;
    min-width: 140px;
    font-size: var(--fs-base);
}

/* Label/value pairs run inline as compact columns instead of stacked lines. */
.data-list--grid .setup-box p,
.data-list--grid .location-box p {
    margin-bottom: 0 !important;
    display: inline-flex;
    align-items: baseline;
    gap: var(--s-1);
    white-space: nowrap;
}

.data-list--grid .setup-box-label,
.data-list--grid .location-box .label { color: var(--c-ink-4); }

.data-list--grid .setup-box > .d-flex:last-child,
.data-list--grid .location-box > .d-flex:last-child {
    margin-top: 0 !important;
    margin-left: auto;
    padding-top: 0;
    border-top: none;
    flex: 0 0 auto;
}

.data-list--grid .active-box {
    background-color: var(--c-primary-soft) !important;
    box-shadow: inset 3px 0 0 var(--c-primary) !important;
    border-bottom-color: var(--c-line-soft) !important;
}

/* ---- The toggle -------------------------------------------------------- */

.view-toggle {
    display: inline-flex;
    background: var(--c-surface-2);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    padding: 2px;
    gap: 2px;
}

.view-toggle__btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: transparent;
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    padding: 5px 12px;
    border-radius: var(--r-sm);
    cursor: pointer;
    transition: background-color var(--transition), color var(--transition);
}

.view-toggle__btn:hover { color: var(--c-ink); }

/* The selected view is a raised white chip against the inset track - the
   same affordance as a segmented control, so which view is active is legible
   without relying on colour alone. */
.view-toggle__btn.is-active {
    background: var(--c-surface);
    color: var(--c-primary);
    box-shadow: var(--sh-sm);
}

.view-toggle__btn i { font-size: 0.95em; }

/* The list header: title, count, search and toggle on one baseline. */
.list-toolbar {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    flex-wrap: wrap;
    margin-bottom: var(--s-3);
}

.list-toolbar__title {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    margin: 0;
}

.list-toolbar__title i { color: var(--c-ink-4); }

.list-toolbar__count {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--c-ink-3);
    background: var(--c-surface-2);
    border: 1px solid var(--c-line);
    border-radius: var(--r-full);
    padding: 1px 8px;
}

.list-toolbar__spacer { margin-left: auto; }
.list-toolbar__search { width: 220px; max-width: 100%; }

@media (max-width: 575.98px) {
    .list-toolbar__spacer { margin-left: 0; }
    .list-toolbar__search { width: 100%; }
    .view-toggle__btn span { display: none; }   /* icons only on a phone */
    .view-toggle__btn { padding: 6px 10px; }

    /* A grid row cannot stay one line on a phone; let it wrap back to a
       stacked card rather than scrolling sideways. */
    .data-list--grid .setup-box,
    .data-list--grid .location-box { flex-direction: column; align-items: flex-start; gap: var(--s-1); }
    .data-list--grid .setup-box h6 { flex: 1 1 auto; }
    .data-list--grid .setup-box > .d-flex:last-child { margin-left: 0; margin-top: var(--s-2) !important; }
}


/* ============================================================================
   7. BADGES AND STATUS
   ----------------------------------------------------------------------------
   Soft-filled rather than solid. A list of twenty solid badges is the single
   biggest source of "too colourful".
   ========================================================================== */

.badge {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    padding: 3px 8px;
    border-radius: var(--r-sm);
    border: 1px solid transparent;
    letter-spacing: 0.01em;
}

.badge.bg-primary, .badge.bg-dark {
    background-color: var(--c-surface-2) !important;
    border-color: var(--c-line-strong);
    color: var(--c-ink-2) !important;
}
.badge.bg-secondary {
    background-color: var(--c-surface-2) !important;
    border-color: var(--c-line-strong);
    color: var(--c-ink-3) !important;
}
.badge.bg-success {
    background-color: var(--c-success-soft) !important;
    border-color: var(--c-success-line);
    color: var(--c-success) !important;
}
.badge.bg-danger {
    background-color: var(--c-danger-soft) !important;
    border-color: var(--c-danger-line);
    color: var(--c-danger) !important;
}
.badge.bg-warning {
    background-color: var(--c-warning-soft) !important;
    border-color: var(--c-warning-line);
    color: var(--c-warning) !important;
}
.badge.bg-info {
    background-color: var(--c-info-soft) !important;
    border-color: var(--c-info-line);
    color: var(--c-info) !important;
}

.alert {
    border-radius: var(--r-md);
    border-width: 1px;
    font-size: var(--fs-base);
    padding: var(--s-3) var(--s-4);
}
.alert-success { background: var(--c-success-soft); border-color: var(--c-success-line); color: var(--c-success); }
.alert-danger  { background: var(--c-danger-soft);  border-color: var(--c-danger-line);  color: var(--c-danger); }
.alert-warning { background: var(--c-warning-soft); border-color: var(--c-warning-line); color: var(--c-warning); }
.alert-info    { background: var(--c-info-soft);    border-color: var(--c-info-line);    color: var(--c-info); }


/* ============================================================================
   8. MODALS AND OVERLAYS
   ========================================================================== */

.modal-backdrop { z-index: var(--z-backdrop); }
.modal { z-index: var(--z-modal); }

.modal-content {
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-lg);
}

.modal-header {
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
}

.modal-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--c-ink); }
.modal-body { padding: var(--s-4); }

.modal-footer {
    background: var(--c-surface-2);
    border-top: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
    gap: var(--s-2);
}

/* Hand-rolled overlays used throughout the app. Named here so every one of
   them sits at the same height and uses the same scrim. */
.modal-overlay,
.rpt-overlay,
.tbl-act-overlay,
.app-overlay {
    position: fixed;
    inset: 0;
    background: rgba(17, 24, 39, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--s-4);
    z-index: var(--z-modal);
}

/* A dialog must not clip what it contains. `overflow: hidden` on a dialog is
   what used to cut off anything that tried to extend past its edge. */
.modal-content-custom,
.rpt-dialog,
.tbl-act-dialog {
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-lg);
    display: flex;
    flex-direction: column;
    max-height: 92vh;
    width: 100%;
    overflow: visible;
}

/* Only the scrolling body clips, which is the one place it is wanted. */
.modal-body-custom {
    overflow-y: auto;
    overflow-x: hidden;
    flex: 1;
    padding: var(--s-4);
}

/* Because the dialogs above no longer clip their children, the first and last
   bands have to round their own outer corners - otherwise a filled header
   renders square and overhangs the dialog's rounded edge. */
.modal-header-compact,
.rpt-header,
.tbl-act-header,
.od-header,
.poup-header,
.split-header {
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.modal-footer-compact,
.rpt-footer,
.tbl-act-footer {
    border-radius: 0 0 var(--r-lg) var(--r-lg);
    background: var(--c-surface-2);
    border-top: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
    display: flex;
    gap: var(--s-2);
    justify-content: flex-end;
}

.modal-title-compact,
.rpt-title,
.tbl-act-title {
    margin: 0;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    display: flex;
    align-items: center;
    gap: var(--s-2);
}

/* Heading icons took the old gold accent. Muted so the title reads first. */
.modal-title-compact i,
.rpt-title i,
.tbl-act-title i { color: var(--c-ink-4); }

.btn-close-custom {
    background: transparent;
    border: none;
    color: var(--c-ink-3);
    width: 28px;
    height: 28px;
    border-radius: var(--r-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color var(--transition), color var(--transition);
}
.btn-close-custom:hover { background: var(--c-line-soft); color: var(--c-ink); }

/* A filter strip between header and body. */
.modal-filter-section {
    padding: var(--s-3) var(--s-4);
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
}


/* ============================================================================
   9. RADZEN OVERLAY COMPONENTS
   ----------------------------------------------------------------------------
   THE DROPDOWN-BEHIND-MODAL FIX.

   Radzen.Blazor's own stylesheet is loaded by the layouts
   (MainLayout.razor / AccountLayout.razor link
   _content/Radzen.Blazor/css/default-base.css), which gives its components
   their structure. Because a layout renders that <link> late, it lands AFTER
   this file - so Radzen wins every ordinary property here, and the rules
   below are only the two things this application has to assert:

     1. STACKING. Radzen appends an open panel to document.body, so `overflow`
        on an ancestor was never what hid it - the panels simply lost to
        dialogs that had hand-picked z-index values of 9999 and 10000. Those
        overlays are now on --z-modal (2000) and panels are pinned here to
        --z-popup (3000) with !important, so the order holds no matter which
        combination is open and regardless of stylesheet order. Tooltips sit
        above both.

     2. BRAND. A light tint on the highlighted row so selection matches the
        rest of the application.

   Two pages worked around the stacking with PopupStyle="z-index: 99999" on
   individual controls; those patches were removed.

   If Radzen's stylesheet is ever dropped from the layouts again, the surface
   rules below (background, border, radius) become load-bearing rather than
   redundant - that is why they are still here.
   ========================================================================== */

.rz-popup,
.rz-dropdown-panel,
.rz-multiselect-panel,
.rz-autocomplete-panel,
.rz-lookup-panel,
.rz-overlaypanel,
.rz-datepicker-popup-container,
.rz-colorpicker-popup,
body > .rz-popup,
body > [class*="rz-"][class*="-panel"] {
    z-index: var(--z-popup) !important;
    background: var(--c-surface);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    box-shadow: var(--sh-lg);
    overflow: auto;
    max-height: 280px;
}

.rz-tooltip,
.rz-tooltip-content {
    z-index: var(--z-tooltip) !important;
}

.rz-tooltip-content {
    background: var(--c-ink);
    color: #fff;
    font-size: var(--fs-sm);
    padding: var(--s-1) var(--s-2);
    border-radius: var(--r-sm);
}

/* Items inside an open panel. Without these the list renders as unstyled
   text with no hit area and no hover feedback. */
.rz-dropdown-item,
.rz-multiselect-item,
.rz-autocomplete-item,
.rz-lookup-item {
    padding: 7px 10px;
    font-size: var(--fs-base);
    color: var(--c-ink-2);
    cursor: pointer;
    list-style: none;
    transition: background-color var(--transition);
}

.rz-dropdown-item:hover,
.rz-multiselect-item:hover,
.rz-autocomplete-item:hover,
.rz-lookup-item:hover { background: var(--c-surface-2); }

.rz-dropdown-item.rz-state-highlight,
.rz-multiselect-item.rz-state-highlight,
.rz-state-highlight {
    background: var(--c-accent-soft);
    color: var(--c-ink);
}

.rz-dropdown-items,
.rz-multiselect-items,
.rz-autocomplete-items { margin: 0; padding: var(--s-1); }

/* ---- RadzenDropDownDataGrid --------------------------------------------
   This variant puts a whole data grid inside the panel rather than a plain
   list, so the item rules above do not reach it. With no Radzen stylesheet
   loaded its table had no layout at all: the header text stacked at the top
   and the cell values drifted to the left of the panel, which is the
   "dropdown looks broken" report on the Order details popup.

   The panel is given a sane width and its inner grid real table layout, so
   a row reads as a row. */
.rz-dropdown-panel:has(.rz-datatable),
.rz-dropdown-panel .rz-datatable {
    min-width: 240px;
}

.rz-dropdown-panel .rz-datatable,
.rz-dropdown-panel table {
    width: 100%;
    border-collapse: collapse;
    table-layout: auto;
}

.rz-dropdown-panel .rz-datatable-thead > tr > th {
    text-align: left;
    padding: var(--s-2) var(--s-3);
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: var(--c-ink-3);
    white-space: nowrap;
}

.rz-dropdown-panel .rz-datatable-tbody > tr > td {
    text-align: left;
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--c-line-soft);
    font-size: var(--fs-base);
    color: var(--c-ink-2);
    white-space: nowrap;
}

.rz-dropdown-panel .rz-datatable-tbody > tr { cursor: pointer; }
.rz-dropdown-panel .rz-datatable-tbody > tr:hover > td { background: var(--c-surface-2); }
.rz-dropdown-panel .rz-datatable-tbody > tr.rz-state-highlight > td {
    background: var(--c-primary-soft);
    color: var(--c-ink);
}

/* The grid's own filter row, which otherwise collapses onto the header. */
.rz-dropdown-panel .rz-datatable-thead .rz-column-filter,
.rz-dropdown-panel .rz-datatable-filter-row > th { padding: var(--s-1) var(--s-2); }
.rz-dropdown-panel .rz-datatable-filter-row input { width: 100%; }

/* A data grid panel needs more room than a plain list. */
.rz-dropdown-panel:has(.rz-datatable) { max-height: 340px; }

/* Panel filter box */
.rz-dropdown-filter-container,
.rz-multiselect-filter-container {
    padding: var(--s-2);
    border-bottom: 1px solid var(--c-line);
}
.rz-dropdown-filter,
.rz-multiselect-filter {
    width: 100%;
    font-size: var(--fs-base);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-sm);
    padding: 5px 8px;
}

/* The closed control, so it matches a .form-control beside it. */
.rz-dropdown,
.rz-multiselect,
.rz-autocomplete,
.rz-lookup {
    background: var(--c-surface);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    font-size: var(--fs-base);
    color: var(--c-ink);
    min-height: 34px;
    display: inline-flex;
    align-items: center;
    width: 100%;
}

/* The closed control carries no padding of its own: the label inside it does,
   so that the selected text starts on exactly the same pixel as the text in a
   plain .form-control beside it. The control used to add 8px here while the
   label added its own on top, which is what pushed the selected value out of
   line with every other field in the row. */
.rz-dropdown.form-control,
.rz-dropdown.form-control-md { padding: 0; }

/* And for the ones that never got the class.
   `.rz-dropdown.form-control` outranks Radzen's own `.rz-dropdown { padding }`
   on specificity, which is the only reason the rule above works - so a picker
   written as `<RadzenDropDown Style="width:100%" />`, with no form-control,
   kept Radzen's padding AND got the inner label's 7px/10px on top of it. In a
   34px control that pushes the chosen value down and out of its own box; on
   the narrow fields (the recipe editor's Unit column, before it moved to
   AppSelect) the value was visibly clipped. Asserted for the same reason
   everything else in section 9 is: Radzen's stylesheet is linked after this
   file, so specificity alone cannot win it back.

   The label keeps the padding. Only the shell gives it up - and only for the
   two controls whose inner label theme.css pads itself (.rz-dropdown-label,
   .rz-multiselect-label). Autocomplete and lookup are left alone: their inner
   input is not padded here, so zeroing their shell would take the padding away
   altogether rather than move it. */
.rz-dropdown,
.rz-multiselect { padding: 0 !important; }

/* Matches .form-control's 7px 10px exactly. The trigger arrow is the only
   thing allowed to sit outside it. */
.rz-dropdown-label,
.rz-dropdown .rz-inputtext,
.rz-multiselect-label,
.rz-autocomplete .rz-inputtext {
    padding: 7px 10px;
    line-height: 1.35;
    min-height: 0;
    /* A long value must not push the arrow off the control or make the field
       grow; it truncates, and the title attribute Radzen sets still shows it
       in full. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* RadzenNumeric given class="form-control" had the same double padding the
   dropdowns above had: the shell took .form-control's 7px/10px and Radzen's
   inner input added 7px of its own, leaving a 15px-tall box for a 17px line.
   The number sat low and was clipped, and the control came out 32px tall
   beside a 35px AppSelect in the same row (the recipe editor's Qty / Unit /
   Wastage columns). The shell gives up its padding; the inner input carries
   it, sized so the control lines up with .app-select__trigger exactly. The
   right padding leaves room for Radzen's up/down buttons. */
.rz-numeric.form-control {
    padding: 0 !important;
    min-height: 35px;
}

.rz-numeric.form-control > .rz-numeric-input {
    box-sizing: border-box;
    height: 33px !important;
    padding: 6px 26px 6px 10px !important;
    line-height: 21px;
    font-size: var(--fs-base);
}

/* RadzenDatePicker with class="form-control" was a box inside a box: the
   shell drew .form-control's border and padding round Radzen's own bordered
   input, giving a 47px field beside 35px ones (the reservation form's Date,
   and its filter bar). The shell becomes a bare wrapper and the input is the
   control, with the same border, height and focus ring as everything else. */
.rz-datepicker.form-control {
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
}

.rz-datepicker.form-control > .rz-inputtext {
    box-sizing: border-box;
    width: 100%;
    height: 35px !important;
    padding: 6px 32px 6px 10px !important;
    line-height: 21px;
    font-size: var(--fs-base);
    background: var(--c-surface);
    border: 1px solid var(--c-line-strong) !important;
    border-radius: var(--r-md);
}

.rz-datepicker.form-control > .rz-inputtext:focus {
    outline: none;
    border-color: var(--c-primary) !important;
    box-shadow: 0 0 0 3px var(--c-primary-soft);
}

.rz-datepicker.form-control > .rz-inputtext:disabled {
    background: var(--c-surface-2);
    color: var(--c-ink-3);
}

/* RadzenTextBox is a single input, but Radzen's own padding beat
   .form-control's, leaving it 31px beside the 35px controls above. */
input.rz-textbox.form-control {
    box-sizing: border-box;
    height: 35px;
    padding: 6px 10px !important;
    line-height: 21px;
}

/* The placeholder has to read as a placeholder, not as a chosen value - the
   same distinction .form-control::placeholder makes. */
.rz-dropdown-label.rz-placeholder,
.rz-multiselect-label.rz-placeholder { color: var(--c-ink-4); }

/* Disabled dropdowns matched neither the enabled control nor a disabled
   input. */
.rz-dropdown.rz-state-disabled,
.rz-multiselect.rz-state-disabled {
    background-color: var(--c-surface-2);
    color: var(--c-ink-3);
    cursor: not-allowed;
    opacity: 1;
}

/* Validation state, so a required dropdown that fails looks like a required
   text box that fails instead of looking untouched. */
.rz-dropdown.input-validation-error,
.rz-dropdown.is-invalid,
.modified.invalid > .rz-dropdown {
    border-color: var(--c-danger, #b42318);
}

.rz-dropdown:focus-within,
.rz-multiselect:focus-within {
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px var(--c-accent-soft);
}

.rz-dropdown-trigger,
.rz-multiselect-trigger {
    color: var(--c-ink-3);
    padding: 0 var(--s-2);
    margin-left: auto;
}

/* The inner label/input is a plain surface inside the control and carries no
   border or fill of its own.

   Padding is deliberately NOT set here. It is set forty lines above to match
   .form-control's 7px 10px, and re-declaring it at equal specificity meant
   this rule won on source order alone: every selected dropdown value in the
   application sat hard against the control's left edge with no left padding,
   while the plain text input beside it kept its 10px. That is what made the
   dropdowns look misaligned against their neighbours - and on the narrower
   controls (the Warehouse field on the adjustment dialog, the recipe form's
   selects) it read as the value not being shown properly at all. */
.rz-dropdown-label,
.rz-inputtext { border: none; background: transparent; width: 100%; }

/* Native selects and Bootstrap dropdowns opened from inside a dialog. Unlike
   Radzen's, these DO render inside their parent, so they need the dialog not
   to clip them (handled above) and a z-index of their own. */
.modal .dropdown-menu,
.modal-content-custom .dropdown-menu,
.rpt-dialog .dropdown-menu,
.dropdown-menu.show {
    z-index: var(--z-popup);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    box-shadow: var(--sh-lg);
    font-size: var(--fs-base);
    padding: var(--s-1);
}

.dropdown-item { border-radius: var(--r-sm); padding: 6px 10px; font-size: var(--fs-base); }
.dropdown-item:hover, .dropdown-item:focus { background: var(--c-surface-2); color: var(--c-ink); }

/* Toast and loader sit above everything, including an open dropdown. */
#toast-container { z-index: var(--z-toast) !important; }
.loader-overlay, #loaderOverlay { z-index: var(--z-loader) !important; }

/* Radzen data grid, toned to match .table above. */
.rz-datatable-thead > tr > th {
    background: var(--c-surface-2);
    color: var(--c-ink-3);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: var(--fw-semibold);
    border-bottom: 1px solid var(--c-line-strong);
    padding: var(--s-2) var(--s-3);
}

.rz-datatable-tbody > tr > td {
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--c-line-soft);
    font-size: var(--fs-base);
}

.rz-datatable-tbody > tr:hover > td { background: var(--c-surface-2); }

.rz-paginator {
    background: var(--c-surface-2);
    border-top: 1px solid var(--c-line);
    padding: var(--s-2);
    font-size: var(--fs-sm);
}

/* ---- 9b. Radzen typography, asserted --------------------------------------
   Radzen's own stylesheet is linked by the layouts and therefore lands after
   this file, so it wins any ordinary declaration - including the
   `font-size: var(--fs-base)` set on .rz-dropdown above, which is why a
   Radzen picker rendered at 12.25px while the plain .form-control sitting in
   the very same form row rendered at 14px. On the setup pages those two
   controls are side by side, so the mismatch is visible in a single glance.

   This is the same situation as the z-index fix at the top of the section: the
   application has to assert, and !important is the only thing that outranks a
   later stylesheet. Kept to typography alone - everything else Radzen brings
   is still allowed to win. */
.rz-dropdown,
.rz-multiselect,
.rz-autocomplete,
.rz-lookup,
.rz-textbox,
.rz-inputtext,
.rz-dropdown-label,
.rz-dropdown-item,
.rz-multiselect-item,
.rz-autocomplete-list-item,
.rz-cell-data,
.rz-datatable-data td,
.rz-grid-table td {
    font-family: var(--f-sans) !important;
    font-size: var(--fs-base) !important;
}

/* Radzen's DataGrid column headers came out at 10.5px - the smallest text
   anywhere in the application, on the row that tells you what every column
   underneath it means. One step down from the cells, not four. */
.rz-column-title,
.rz-column-title-content,
.rz-datatable-thead th,
.rz-grid-table thead th {
    font-family: var(--f-sans) !important;
    font-size: var(--fs-sm) !important;
}

/* ---- 9c. Radzen brand colour, asserted ------------------------------------
   Radzen ships a cool blue (#35a0d7 / #3193c6) for every stateful control -
   the tick in a checkbox, a selected radio, the up/down on a numeric stepper,
   today's date in a calendar, the highlighted row in an open dropdown. Because
   its stylesheet loads after this one those blues were showing up inside forms
   whose buttons and headings are brand rose, so a single form could contain
   two unrelated accent colours.

   Same !important reasoning as 9a/9b: a later stylesheet cannot be beaten any
   other way. */
.rz-chkbox-box.rz-state-active,
.rz-radiobutton-box.rz-state-active,
.rz-switch.rz-switch-checked .rz-switch-circle,
.rz-slider-handle,
.rz-slider-range {
    background-color: var(--c-primary) !important;
    border-color: var(--c-primary) !important;
}

.rz-chkbox-box.rz-state-active .rz-chkbox-icon,
.rz-radiobutton-box.rz-state-active .rz-radiobutton-icon {
    color: var(--c-on-primary) !important;
}

.rz-numeric-button,
.rz-button.rz-secondary {
    background-color: var(--c-surface-2) !important;
    border-color: var(--c-line) !important;
    color: var(--c-ink-2) !important;
}

.rz-numeric-button:hover,
.rz-button.rz-secondary:hover {
    background-color: var(--c-primary-soft) !important;
    color: var(--c-primary) !important;
}

.rz-calendar-today > .rz-state-default,
.rz-datepicker-today > span,
.rz-state-highlight {
    background-color: var(--c-primary-soft) !important;
    color: var(--c-primary) !important;
}

.rz-dropdown-item.rz-state-highlight,
.rz-multiselect-item.rz-state-highlight {
    background-color: var(--c-primary-soft) !important;
    color: var(--c-primary) !important;
}

/* Bootstrap sizes this 0.875rem, which lands off the scale. */
.dropdown-header { font-size: var(--fs-sm); }

/* toastr ships its own 0.875rem. A toast is often the only confirmation that
   a save worked, so it is not the place to be a size smaller than everything
   else. */
.toast-message { font-size: var(--fs-base); }
.toast-title { font-size: var(--fs-base); font-weight: var(--fw-semibold); }
#toast-container > div { font-family: var(--f-sans); }


/* ============================================================================
   10. LEGACY TOKEN BRIDGES
   ----------------------------------------------------------------------------
   The Order screens were built on a maroon-and-gold palette declared inside
   Order.razor, and its sibling components reference those variables with a
   hard-coded maroon fallback - var(--ord-maroon, #800020) - so they stayed
   maroon on any page where Order.razor had not rendered.

   Redefining the names here, globally, retones every one of those screens
   without touching their markup. The names are kept deliberately: renaming
   them across ~2,900 lines of component CSS would be churn with no visual
   benefit, and this way the fallbacks never fire.
   ========================================================================== */

:root {
    --ord-maroon:       var(--c-primary);
    --ord-maroon-dark:  var(--c-primary-hover);
    --ord-maroon-deep:  var(--auth-primary-deep);
    --ord-maroon-light: color-mix(in srgb, var(--c-primary) 78%, var(--auth-surface));
    --ord-maroon-tint:  var(--c-primary-soft);

    /* Gold was the accent on nearly every heading icon. Neutralised: icons
       now take their colour from the text around them. */
    --ord-gold:         var(--c-ink-4);
    --ord-gold-dark:    var(--c-ink-3);
    --ord-gold-tint:    var(--c-surface-2);

    --ord-ink:          var(--c-ink);
    --ord-ink-soft:     var(--c-ink-3);
    --ord-line:         var(--c-line);
    --ord-line-soft:    var(--c-surface-2);
    --ord-bg:           var(--c-bg);
    --ord-surface:      var(--c-surface);
    --ord-slate-tint:   var(--c-surface-2);

    --ord-danger:       var(--c-danger);
    --ord-danger-tint:  var(--c-danger-soft);
    --ord-ok:           var(--c-success);
    --ord-ok-tint:      var(--c-success-soft);
}

/* The old highlight was a 2px orange border on a cream fill. */
.active-box {
    border: 1px solid var(--c-primary) !important;
    background-color: var(--c-primary-soft) !important;
    box-shadow: var(--sh-sm) !important;
}

.label { font-weight: var(--fw-medium); color: var(--c-ink-3); }
.value { color: var(--c-ink); }

/* Bootstrap's "dark" utilities are pure black (#212529 / #000). Pages reach
   for them to mean "strong", not "black", so they are remapped onto the warm
   brand ink rather than left to punch a hole in a warm palette. */
.text-dark { color: var(--c-ink) !important; }
.border-dark { border-color: var(--c-line-strong) !important; }
.bg-dark { background-color: var(--c-ink) !important; }
.text-black, .text-body { color: var(--c-ink) !important; }
.text-secondary { color: var(--c-ink-3) !important; }


/* ============================================================================
   11. RESPONSIVE
   ========================================================================== */

@media (max-width: 991.98px) {
    .card-body { padding: var(--s-3); }
    .modal-overlay,
    .rpt-overlay,
    .tbl-act-overlay,
    .app-overlay { padding: var(--s-2); }
}

@media (max-width: 767.98px) {
    /* The base used to be stepped down here. It is not any more: a phone is
       held further from nothing and closer to everything, and shrinking text
       on the narrowest screen is backwards. The scale is now the same at every
       width, which is the point of having one. Narrow layouts are handled by
       reflowing below, not by making the type smaller. */

    .card { border-radius: var(--r-md); }
    .card-header, .card-body, .card-footer { padding: var(--s-3); }

    /* Dialogs go effectively full-screen rather than leaving a sliver of
       scrim on each side. */
    .modal-content-custom,
    .rpt-dialog,
    .tbl-act-dialog { max-height: 96vh; border-radius: var(--r-md); }

    /* Any table wider than the viewport scrolls in place. The page body must
       never scroll sideways. */
    .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* Footer buttons stack and go full width, so they stay tappable. */
    .modal-footer,
    .modal-footer-compact { flex-wrap: wrap; }
    .modal-footer .btn,
    .modal-footer-compact .btn { flex: 1 1 auto; }

    .btn { padding: 8px 14px; }

    /* The top bar is a flex row whose children had no permission to shrink,
       so on a narrow screen the left-hand side kept its full width and pushed
       the action buttons past the right edge - 26px of horizontal scroll on
       every page at 375px.

       A flex item's default min-width is `auto`, which refuses to shrink
       below its content; min-width: 0 is what actually lets it compress. */
    .app-header { max-width: 100vw; overflow-x: clip; }
    .app-header > * { min-width: 0; }
    .header-actions { flex-shrink: 1; min-width: 0; gap: 2px; }
    .header-actions > * { min-width: 0; flex-shrink: 1; }

    /* The profile control carries an avatar, a name and a chevron. The name
       and chevron are the parts worth dropping first on a phone. */
    .profile-wrapper, .profile-trigger { min-width: 0; max-width: 100%; }
    .profile-arrow { display: none; }

    /* Keep the profile menu inside the viewport rather than letting a fixed
       260px panel hang off the edge. */
    .profile-dropdown {
        width: min(260px, calc(100vw - 16px)) !important;
        right: 4px;
    }
}

/* A page must never scroll sideways. This is the safety net; the rules above
   are the actual fix, because relying on this alone silently hides content
   that has overflowed. */
body { overflow-x: hidden; }

/* ---- 12. Report and ledger toolbars ---------------------------------------
   The compact "title on the left, filters and Search/Reset on the right" header
   shared by every report and payment-ledger screen.

   This lived in a <style> block inside FromToEndReport.razor and, copied
   verbatim, CustomerDueReport.razor. Two problems with that:

     * A <style> inside a .razor is only in the DOM while that component is
       rendered, so every other screen using these class names - the sales,
       ageing, summary, payment-history and pending-payments pages - rendered
       with no styling at all.
     * That block also redefined the global .card-header with !important
       (white background, 2px border), so opening a report silently restyled
       every card header in the application until you navigated away.

   Defining it once here fixes both, and gives the ERP screens the consistent
   control heights the rest of this file already sets: 32px filter controls
   against the 34px form controls in section 4, so a filter bar reads as
   secondary to a form without being a different design.
--------------------------------------------------------------------------- */

.header-title {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink);
    display: flex;
    align-items: center;
    margin: 0;
}

.header-title i { color: var(--c-accent); }

.filter-controls {
    display: flex;
    align-items: center;
    gap: var(--s-3, 12px);
    flex-wrap: wrap;
}

.input-group-compact {
    display: flex;
    align-items: center;
    gap: 6px;
}

.input-label {
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
    font-weight: var(--fw-semibold, 600);
    margin: 0;
    white-space: nowrap;
}

/* Sized rather than !important-ed: these are the only .form-controls inside a
   toolbar, so a plain descendant selector is specific enough. */
.input-group-compact .form-control,
.input-group-compact .form-select,
.input-group-compact .app-select__trigger {
    width: 140px;
    height: 32px;
    padding: 4px 8px;
    font-size: var(--fs-base);
    border-radius: var(--r-md);
}

.btn-group-compact {
    display: flex;
    gap: var(--s-2, 8px);
}

.btn-group-compact .btn {
    height: 32px;
    padding: 4px 14px;
    font-size: var(--fs-base);
    font-weight: var(--fw-medium, 500);
    border-radius: var(--r-md);
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
}

.btn-group-compact .btn i { font-size: var(--fs-sm); }

@media (max-width: 1200px) {
    .input-group-compact .form-control,
    .input-group-compact .form-select,
    .input-group-compact .app-select__trigger { width: 120px; }
}

/* Below this the toolbar is taller than the content it filters, so it stacks
   and the controls go full width rather than shrinking to unusable slivers. */
@media (max-width: 700px) {
    .card-header .filter-controls { width: 100%; }

    .input-group-compact { flex: 1 1 100%; }

    .input-group-compact .form-control,
    .input-group-compact .form-select,
    .input-group-compact .app-select__trigger { width: 100%; }

    .btn-group-compact { width: 100%; }
    .btn-group-compact .btn { flex: 1; justify-content: center; }
}


/* ---- 12a. Page shell (Components/Common/Pages/AppPage.razor) --------------
   The payment and report screens rendered straight into `.main`, which has
   no padding - so every title sat flush against the left edge of the window
   and each card spaced itself with its own `mt-2`. AppPage gives them the
   gutter the stock screens get from `.stock-page`, a single vertical rhythm,
   and one filter-bar layout.

   Everything below is scoped to `.app-page`, so the other screens that share
   the section-12 toolbar classes (users, notifications, recycle bin) keep
   exactly the layout they have.
--------------------------------------------------------------------------- */

.app-page {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    padding: var(--s-5) var(--s-6) var(--s-10);
    display: flex;
    flex-direction: column;
    gap: var(--s-4);
}

/* The shell owns the spacing between blocks; the header's own bottom margin
   and the cards' `mt-2` would double it. */
.app-page > .page-head { margin-bottom: 0; }
.app-page > .card,
.app-page > section,
.app-page > div { margin-top: 0 !important; }

/* One card language: hairline border, small shadow, rounded - rather than
   `shadow-sm border-0`, which drew a borderless box that dissolved into the
   page background. */
.app-page > .card {
    border: 1px solid var(--c-line) !important;
    border-radius: var(--r-lg);
    box-shadow: var(--sh-sm) !important;
    /* Not overflow: hidden - the filter bar's AppSelect panels are
       positioned inside the card and would be clipped by it. */
}

/* The filter strip at the top of a report or ledger card. */
.app-page .card > .card-header {
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
    padding: var(--s-3) var(--s-4);
}

/* Labels above their controls, buttons aligned to the controls' baseline.
   Side-by-side labels made every field a different width and pushed the
   Search button onto its own line at ordinary window sizes. */
.app-page .filter-controls,
.app-page .filter-controls.align-items-center {
    /* The pages also carry Bootstrap's align-items-center, which is
       !important - so this has to be too, or the buttons float half a
       label above the controls they belong to. */
    align-items: flex-end !important;
    gap: var(--s-3) !important;
}

.app-page .input-group-compact {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
}

.app-page .input-label {
    font-size: var(--fs-xs);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--c-ink-3);
}

.app-page .input-group-compact .form-control,
.app-page .input-group-compact .form-select,
.app-page .input-group-compact .app-select__trigger {
    width: 100%;
    min-width: 150px;
}

.app-page .btn-group-compact { align-items: flex-end; }

/* Loading and empty states inside a card: centred, quiet, and the same on
   every screen. The pages still render their own text; this only gives it
   a consistent frame. */
.app-page .card > .text-center.py-5 {
    padding: var(--s-10) var(--s-4) !important;
    color: var(--c-ink-3);
}

.app-page .card > .text-center.py-5 > .mb-3 > i {
    color: var(--c-line-strong) !important;
}

@media (max-width: 768px) {
    .app-page { padding: var(--s-3) var(--s-3) var(--s-8); gap: var(--s-3); }
}

@media (max-width: 700px) {
    .app-page .input-group-compact .form-control,
    .app-page .input-group-compact .form-select,
    .app-page .input-group-compact .app-select__trigger { min-width: 0; }
}

/* Print: the shell's gutter is the browser's margin on paper. */
@media print {
    .app-page { padding: 0; gap: var(--s-3); }
    .app-page > .card { box-shadow: none !important; }
}


/* ---- 13. Report grid -------------------------------------------------------
   The billing transaction grid (PartialBillingTransactionReport), and the
   summary and refine bars above it.

   This replaces a <style> block that lived inside the component. Besides the
   scoping problem described at the top of section 12, that block styled the
   grid as a dark navy (#1a1a2e) table with its own blues and greys - a palette
   that appears nowhere else in the product, so the one screen a manager looks
   at every morning was the one screen that did not look like the application.
   Everything below is built from the tokens in section 1, so the grid retones
   with the rest of the product.

   It is deliberately close to .ss-table on the sales summary pages: the two
   reports are read the same way and should not feel like different software.
--------------------------------------------------------------------------- */

/* ---- Summary tiles ---- */

.rpt-totals {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2, 8px);
    padding: var(--s-3, 12px) var(--s-4, 16px);
    border-bottom: 1px solid var(--c-line);
}

.rpt-total {
    flex: 1 1 130px;
    min-width: 0;
    background: var(--c-surface-2);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    padding: 8px 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* The number the page exists to show. */
.rpt-total--key {
    background: var(--c-accent-soft);
    border-color: var(--c-accent-line);
}

.rpt-total--alert { border-color: var(--c-danger-line); }

.rpt-total__label {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink-3);
    white-space: nowrap;
}

.rpt-total__value {
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--c-ink);
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rpt-total__value--neg { color: var(--c-danger); }
.rpt-total__value--pos { color: var(--c-success); }

.rpt-total__note {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
    white-space: nowrap;
}

/* ---- Takings by payment method ---- */

.rpt-breakdown {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2, 8px);
    padding: 10px var(--s-4, 16px);
    border-bottom: 1px solid var(--c-line);
}

.rpt-breakdown__title {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink-3);
    margin-right: 4px;
}

/* A button, not a chip: each one filters the grid to that method, which is how
   a cashier checks the drawer against the report. */
.rpt-breakdown__item {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    padding: 4px 10px;
    border: 1px solid var(--c-line);
    border-radius: var(--r-full);
    background: var(--c-surface);
    cursor: pointer;
    transition: var(--transition);
    font-family: inherit;
}

.rpt-breakdown__item:hover {
    border-color: var(--c-accent-line);
    background: var(--c-accent-soft);
}

.rpt-breakdown__item.is-active {
    border-color: var(--c-accent);
    background: var(--c-accent-soft);
    box-shadow: 0 0 0 2px var(--c-ring);
}

.rpt-breakdown__method {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink);
}

.rpt-breakdown__amount {
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--c-ink);
    font-variant-numeric: tabular-nums;
}

.rpt-breakdown__count {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
    background: var(--c-surface-2);
    border-radius: var(--r-full);
    padding: 0 6px;
}

/* ---- Clickable figures ----
   A tile with records behind it is a <button>: it opens those records in the
   side panel (ReportDetailDrawer). It keeps the tile's look and gains only a
   chevron, a hover border and a focus ring, so the row of tiles still reads
   as one row. */

button.rpt-total {
    font: inherit;
    text-align: left;
    color: inherit;
    appearance: none;
}

.rpt-total--link {
    cursor: pointer;
    transition: border-color var(--transition), box-shadow var(--transition), background-color var(--transition);
}

.rpt-total--link .rpt-total__label i {
    font-size: 9px;
    margin-left: 3px;
    opacity: 0.45;
    transition: transform var(--transition), opacity var(--transition);
}

.rpt-total--link:hover:not(:disabled) {
    border-color: var(--c-accent-line);
    box-shadow: var(--sh-sm);
}

.rpt-total--link:hover:not(:disabled) .rpt-total__label i { opacity: 1; transform: translateX(2px); }

.rpt-total--link:focus-visible {
    outline: none;
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px var(--c-ring);
}

.rpt-total--link:disabled { cursor: default; }

/* ---- Payment summary and order states ---- */

.rpt-overview {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: var(--s-3, 12px);
    padding: var(--s-3, 12px) var(--s-4, 16px);
    border-bottom: 1px solid var(--c-line);
    align-items: start;
}

/* The billing report has the payment summary alone; it should not stretch to
   the full width of a wide monitor, where the amounts drift away from their
   labels. */
.rpt-overview--single { grid-template-columns: minmax(0, 620px); }

.rpt-paysum,
.rpt-states {
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    overflow: hidden;
    min-width: 0;
}

.rpt-paysum__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 12px;
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
}

.rpt-paysum__title {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold, 600);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--c-ink-2);
}

.rpt-paysum__title i { color: var(--c-ink-4); margin-right: 4px; }

.rpt-paysum__hint { font-size: var(--fs-xs); color: var(--c-ink-4); }

.rpt-paysum__empty { margin: 0; padding: 14px 12px; font-size: var(--fs-sm); color: var(--c-ink-3); }

.rpt-paysum__tbl {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
    font-variant-numeric: tabular-nums;
}

.rpt-paysum__tbl th {
    font-size: 11px;
    font-weight: var(--fw-semibold, 600);
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--c-ink-3);
    padding: 6px 12px;
    border-bottom: 1px solid var(--c-line);
}

.rpt-paysum__tbl td {
    padding: 7px 12px;
    border-bottom: 1px solid var(--c-line-soft);
    color: var(--c-ink);
    vertical-align: middle;
}

.rpt-paysum__row,
.rpt-paysum__grand,
.rpt-paysum__due {
    cursor: pointer;
    transition: background-color var(--transition);
}

.rpt-paysum__row:hover,
.rpt-paysum__grand:hover,
.rpt-paysum__due:hover { background: var(--c-accent-soft); }

.rpt-paysum__row:focus-visible,
.rpt-paysum__grand:focus-visible,
.rpt-paysum__due:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: -2px;
}

.rpt-paysum__row.is-active { background: var(--c-accent-soft); box-shadow: inset 3px 0 0 var(--c-accent); }

.rpt-paysum__icon { width: 18px; text-align: center; color: var(--c-ink-3); margin-right: 6px; }
.rpt-paysum__method { font-weight: var(--fw-semibold, 600); }
.rpt-paysum__amt { font-weight: 700; white-space: nowrap; }

.rpt-paysum__go {
    font-size: 9px;
    color: var(--c-ink-4);
    margin-left: 6px;
    opacity: 0;
    transition: opacity var(--transition);
}

.rpt-paysum__row:hover .rpt-paysum__go,
.rpt-paysum__grand:hover .rpt-paysum__go,
.rpt-paysum__due:hover .rpt-paysum__go,
.rpt-paysum__row:focus-visible .rpt-paysum__go,
.rpt-state:hover .rpt-paysum__go { opacity: 1; }

.rpt-paysum__share-col { width: 34%; }

.rpt-paysum__bar {
    display: inline-block;
    vertical-align: middle;
    width: calc(100% - 46px);
    height: 6px;
    border-radius: var(--r-full);
    background: var(--c-line-soft);
    overflow: hidden;
}

.rpt-paysum__bar > span {
    display: block;
    height: 100%;
    background: var(--c-accent);
    border-radius: inherit;
}

.rpt-paysum__pct {
    display: inline-block;
    width: 42px;
    text-align: right;
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.rpt-paysum__sub td { color: var(--c-ink-3); font-size: var(--fs-xs); }

.rpt-paysum__grand td {
    font-weight: 800;
    font-size: var(--fs-base);
    border-top: 2px solid var(--c-line-strong);
    border-bottom: 0;
    background: var(--c-surface-2);
}

.rpt-paysum__grand:hover td { background: var(--c-accent-soft); }

.rpt-paysum__due td { color: var(--c-danger); font-weight: var(--fw-semibold, 600); border-bottom: 0; }

.rpt-paysum__warn {
    margin: 0;
    padding: 8px 12px;
    font-size: var(--fs-xs);
    color: var(--c-warning);
    background: var(--c-warning-soft);
    border-top: 1px solid var(--c-warning-line);
}

.rpt-states { display: flex; flex-direction: column; }

.rpt-state {
    display: grid;
    grid-template-columns: 10px auto 1fr auto 14px;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 12px;
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--c-line-soft);
    font: inherit;
    text-align: left;
    color: var(--c-ink);
    cursor: pointer;
    transition: background-color var(--transition);
}

.rpt-state:last-child { border-bottom: 0; }
.rpt-state:hover:not(:disabled) { background: var(--c-accent-soft); }
.rpt-state:focus-visible { outline: 2px solid var(--c-accent); outline-offset: -2px; }
.rpt-state:disabled { cursor: default; color: var(--c-ink-4); }
.rpt-state:disabled .rpt-paysum__go { display: none; }

.rpt-state__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--c-line-strong); }
.rpt-state.is-settled .rpt-state__dot { background: var(--c-success); }
.rpt-state.is-part .rpt-state__dot    { background: var(--c-warning); }
.rpt-state.is-open .rpt-state__dot    { background: var(--c-info); }
.rpt-state.is-void .rpt-state__dot    { background: var(--c-danger); }

.rpt-state__label { font-size: var(--fs-sm); font-weight: var(--fw-semibold, 600); }

.rpt-state__hint {
    font-size: var(--fs-xs);
    color: var(--c-ink-4);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rpt-state__count { font-size: var(--fs-md); font-weight: 700; font-variant-numeric: tabular-nums; }

@media (max-width: 991.98px) {
    .rpt-overview { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 575.98px) {
    .rpt-paysum__share-col,
    .rpt-paysum__hint,
    .rpt-state__hint { display: none; }
}

/* ---- Drill-down side panel (ReportDetailDrawer) ----
   Docked and animated by drawer.css as an .rpt-dialog; this is only what is
   inside it. The header and footer stay put and the table scrolls. */

.rdd {
    background: var(--c-surface);
    outline: none;
}

.rdd__head {
    flex: none;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--c-line);
}

.rdd__icon {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: var(--r-md);
    background: var(--c-accent-soft);
    color: var(--c-accent-ink);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-md);
}

.rdd__heading { min-width: 0; flex: 1; }

.rdd__title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rdd__sub { margin: 2px 0 0; font-size: var(--fs-xs); color: var(--c-ink-3); }

.rdd__close {
    flex: none;
    width: 32px;
    height: 32px;
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    background: var(--c-surface);
    color: var(--c-ink-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background-color var(--transition), color var(--transition);
}

.rdd__close:hover { background: var(--c-line-soft); color: var(--c-ink); }
.rdd__close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--c-ring); }

.rdd__stats {
    flex: none;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 16px;
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
}

.rdd__stat {
    flex: 1 1 120px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 6px 10px;
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    min-width: 0;
}

.rdd__stat--key { background: var(--c-accent-soft); border-color: var(--c-accent-line); flex-grow: 2; }

.rdd__stat-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink-3);
}

.rdd__stat-value {
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--c-ink);
    font-variant-numeric: tabular-nums;
}

.rdd > .rpt-pager { flex: none; padding-left: 16px; padding-right: 16px; }

.rdd__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    transition: opacity var(--transition);
}

.rdd__body.is-busy { opacity: 0.55; pointer-events: none; }

.rdd__tbl { margin: 0; font-size: var(--fs-sm); }

.rdd__tbl thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--c-surface-2);
    white-space: nowrap;
}

.rdd__tbl tfoot td {
    position: sticky;
    bottom: 0;
    background: var(--c-surface-2);
    font-weight: 700;
    border-top: 2px solid var(--c-line-strong);
    white-space: nowrap;
}

.rdd__tbl td { vertical-align: top; }
.rdd__tbl td.num { white-space: nowrap; }

/* The column that answers the question the panel was opened with. */
.rdd__col-key { background: var(--c-accent-soft); font-weight: 700; }
.rdd__tbl thead th.rdd__col-key { background: var(--c-accent-soft); color: var(--c-accent-ink); }

.rdd__state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 56px 20px;
    text-align: center;
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
}

.rdd__state p { margin: 0; color: var(--c-ink-2); }
.rdd__state i { font-size: 2rem; color: var(--c-line-strong); }
.rdd__state--bad i,
.rdd__state--bad strong { color: var(--c-danger); }

.rdd__foot {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 16px;
    border-top: 1px solid var(--c-line);
    background: var(--c-surface-2);
}

.rdd__foot-note { font-size: var(--fs-xs); color: var(--c-ink-3); }

/* ---- Empty and failed states (additions) ---- */

.rpt-empty--error i { color: var(--c-danger); }
.rpt-empty--error p { color: var(--c-ink-2); }

.rpt-empty__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-top: 8px;
}

/* ---- Refine bar ---- */

.rpt-refine {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2, 8px);
    padding: 10px var(--s-4, 16px);
    border-bottom: 1px solid var(--c-line);
    background: var(--c-surface);
}

.rpt-refine__search {
    position: relative;
    flex: 1 1 300px;
    min-width: 200px;
}

.rpt-refine__search i {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
    pointer-events: none;
}

.rpt-refine__search .form-control {
    height: 32px;
    padding: 4px 10px 4px 30px;
    font-size: var(--fs-base);
    border-radius: var(--r-md);
}

.rpt-refine__select {
    height: 32px;
    padding: 4px 28px 4px 10px;
    font-size: var(--fs-base);
    border-radius: var(--r-md);
    width: auto;
    min-width: 150px;
}

.rpt-refine__check {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-base);
    color: var(--c-ink);
    white-space: nowrap;
    cursor: pointer;
    margin: 0;
}

/* Pushes the count and the action buttons to the right edge, so the controls
   that change the data and the controls that act on it are not interleaved. */
.rpt-refine__spacer { flex: 1 1 auto; }

.rpt-refine__count {
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
    white-space: nowrap;
}

.rpt-refine__count strong { color: var(--c-ink); }

.rpt-refine__btn {
    height: 32px;
    padding: 4px 12px;
    font-size: var(--fs-sm);
    border-radius: var(--r-md);
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
}

/* ---- Table ---- */

.rpt-table-wrap {
    width: 100%;
    overflow-x: auto;
}

/* The grid had its own max-height scroll box here.

   It was removed: a report that scrolls inside a panel while the page behind
   it stays still reads as a stuck page, and it fights the mouse wheel at the
   boundary. Reports now scroll with the page like every other screen.

   The reason the box existed was that sticky column headings otherwise stick
   to the viewport and slide under the application header, which is sticky at
   58px with a higher z-index - leaving a screen of numbers with no labels.
   That is solved directly instead, by sticking the headings BELOW the header
   (--app-header-h) rather than by trapping the table in a box. */
.rpt-table-wrap--scroll {
    /* Retained as a no-op so any page still asking for it is unaffected. */
    overflow: visible;
}

/* The report grid's own scroll box.

   The comment above explains why the page-scroll approach was chosen; this is
   the opposite request, made deliberately: with a page of rows (25, 50, 100)
   the column headings scrolled out of sight and the numbers under them
   stopped meaning anything. The box is capped to roughly the screen, the rows
   scroll inside it, and the headings stick to top:0 OF THE BOX - see the
   .rpt-scrollbox selectors in the sticky-heading block further down, which
   override the below-the-app-header offset used for whole-page scrolling.

   The height is sized in ROWS, not viewport: room for 40 rows plus the
   heading, so up to 40 records show without an inner scrollbar and anything
   beyond that scrolls inside the box with the heading pinned. 48px is the
   tallest row in use (the two-line date cell on the billing grid); the
   denser grids simply fit a few more before scrolling. */
.rpt-scrollbox {
    --rpt-row-h: 48px;
    --rpt-head-h: 40px;
    max-height: calc(var(--rpt-row-h) * 40 + var(--rpt-head-h));
    overflow: auto;
}
.rpt-tbl {
    width: 100%;
    font-size: var(--fs-sm);
    margin: 0;
}

/* Only the all-columns view needs to be wider than the screen. The compact
   view was being forced to min-width:1600px whether it needed it or not, which
   is why this report scrolled sideways even on a wide monitor. */
.rpt-table-wrap.is-wide .rpt-tbl { min-width: 1700px; }

.rpt-tbl thead th {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-3);
    font-weight: var(--fw-semibold, 600);
    white-space: nowrap;
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
    position: sticky;
    top: var(--app-header-h, 88px);
    z-index: var(--z-base, 1);
}

.rpt-sortable { cursor: pointer; user-select: none; }
.rpt-sortable:hover { color: var(--c-ink); }

.rpt-sort { margin-left: 3px; font-size: 10px; }
.rpt-sort--idle { opacity: 0.3; }

.rpt-tbl tbody td {
    font-size: var(--fs-sm);
    white-space: nowrap;
    vertical-align: middle;
}

.rpt-tbl tbody tr:hover { background: var(--c-accent-soft); }

/* A bill that still owes money is the one row type worth spotting from across
   the room; a left border does that without shouting like a filled row. */
.rpt-row--due td:first-child { box-shadow: inset 3px 0 0 var(--c-danger); }

.rpt-bill { font-weight: var(--fw-semibold, 600); color: var(--c-accent-ink); }

.rpt-date__day { display: block; }

.rpt-date__time {
    display: block;
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.rpt-muted { color: var(--c-ink-3); }

/* ─── Ready-made reports ───

   A directory, not a feature panel: the user is scanning for the question
   they want answered, so the card is a name and one line explaining it,
   and nothing competes with that. No accent fills, no shadows - the
   heading row above already says what section this is. */
.rpt-preset-group { border-top: 1px solid var(--c-line); }
.rpt-preset-group:first-child { border-top: 0; }

.rpt-preset-head {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 2px;
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    color: var(--c-ink);
}
.rpt-preset-head:hover .rpt-preset-title { color: var(--c-accent-ink); }

.rpt-preset-chev {
    width: 12px;
    font-size: 11px;
    color: var(--c-ink-3);
    flex: none;
}

.rpt-preset-title {
    font-size: var(--fs-base);
    font-weight: 600;
}

.rpt-preset-count {
    margin-left: auto;
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
    font-variant-numeric: tabular-nums;
}

.rpt-preset-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 8px;
    padding: 0 0 12px 20px;
}

.rpt-preset {
    display: flex;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--c-line);
    border-radius: 8px;
    background: var(--c-surface);
    text-decoration: none;
    color: inherit;
}
.rpt-preset:hover {
    border-color: var(--c-accent);
    background: var(--c-surface-2);
    text-decoration: none;
}

.rpt-preset__icon {
    flex: none;
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    border-radius: 6px;
    background: var(--c-surface-2);
    color: var(--c-accent-ink);
    font-size: 13px;
}
.rpt-preset:hover .rpt-preset__icon { background: var(--c-accent-soft); }

/* min-width:0 so a long description wraps instead of forcing the grid
   column wider than its track. */
.rpt-preset__body { min-width: 0; }

.rpt-preset__name {
    display: block;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-ink);
}

/* Two lines, then ellipsis. Clamped deliberately rather than left to an
   inherited nowrap, which cut the sentence off mid-word on one line and
   made every card look broken at narrow widths. white-space is reset
   explicitly because that inherited rule is what caused it. */
.rpt-preset__desc {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    white-space: normal;
    margin-top: 2px;
    font-size: var(--fs-xs);
    line-height: 1.45;
    color: var(--c-ink-3);
}

/* The name gets one line and must not be the thing that truncates - a
   card whose title is cut is unreadable, where a cut description is
   merely shorter. */
.rpt-preset__name {
    white-space: normal;
    overflow-wrap: anywhere;
}

@media (max-width: 575.98px) {
    .rpt-preset-grid {
        grid-template-columns: 1fr;
        padding-left: 8px;
    }
}

.rpt-tbl td.text-end,
.rpt-tbl th.text-end { font-variant-numeric: tabular-nums; }

.rpt-pay-badge {
    display: inline-block;
    padding: 1px 9px;
    border-radius: var(--r-full);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold, 600);
    background: var(--c-surface-2);
    color: var(--c-ink);
    border: 1px solid var(--c-line);
    white-space: nowrap;
}

.rpt-total-val { font-weight: 700; color: var(--c-ink); }
.rpt-due-val { color: var(--c-danger); font-weight: var(--fw-semibold, 600); }
.rpt-return-val { color: var(--c-success); }

.rpt-tbl__foot td {
    font-weight: 700;
    border-top: 2px solid var(--c-line-strong);
    background: var(--c-surface-2);
    position: sticky;
    bottom: 0;
    white-space: nowrap;
}

.rpt-nomatch {
    text-align: center;
    padding: 32px 16px;
    color: var(--c-ink-3);
    font-size: var(--fs-sm);
    white-space: normal;
}

/* ---- Empty state ---- */

.rpt-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 56px 20px;
    gap: 8px;
    text-align: center;
}

.rpt-empty i { font-size: 2.5rem; color: var(--c-line-strong); }

.rpt-empty p {
    font-size: var(--fs-md);
    font-weight: var(--fw-medium, 500);
    color: var(--c-ink-3);
    margin: 0;
}

.rpt-empty span { font-size: var(--fs-sm); color: var(--c-ink-3); }

/* ---- Date presets ---- */

.rpt-presets {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 8px var(--s-4, 16px);
    border-bottom: 1px solid var(--c-line);
    background: var(--c-surface-2);
}

.rpt-presets__label {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: var(--fw-semibold, 600);
    color: var(--c-ink-3);
    margin-right: 2px;
}

.rpt-preset {
    padding: 3px 11px;
    border: 1px solid var(--c-line);
    border-radius: var(--r-full);
    background: var(--c-surface);
    font-size: var(--fs-sm);
    color: var(--c-ink);
    cursor: pointer;
    white-space: nowrap;
    transition: var(--transition);
    font-family: inherit;
}

.rpt-preset:hover {
    border-color: var(--c-accent-line);
    background: var(--c-accent-soft);
}

.rpt-preset.is-active {
    background: var(--c-accent);
    border-color: var(--c-accent);
    color: var(--c-on-primary);
    font-weight: var(--fw-medium, 500);
}

.rpt-range-note {
    margin-left: auto;
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
    white-space: nowrap;
}

@media (max-width: 900px) {
    .rpt-refine__search { flex: 1 1 100%; }
    .rpt-refine__spacer { display: none; }
    .rpt-range-note { margin-left: 0; flex: 1 1 100%; }
}

/* ---- Ageing badge ---------------------------------------------------------
   How overdue a balance is, on the customer due grid. The bands match the
   buckets the ageing report groups by, so a row flagged red on one screen
   lands in the 90+ column on the other rather than the two disagreeing.
--------------------------------------------------------------------------- */

.rpt-age-badge {
    display: inline-block;
    min-width: 42px;
    padding: 1px 8px;
    border-radius: var(--r-full);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold, 600);
    text-align: center;
    border: 1px solid transparent;
    white-space: nowrap;
}

.rpt-age-badge--current {
    background: var(--c-success-soft);
    color: var(--c-success);
    border-color: var(--c-success-line);
}

.rpt-age-badge--31to60 {
    background: var(--c-warning-soft);
    color: var(--c-warning);
    border-color: var(--c-warning-line);
}

.rpt-age-badge--61to90 {
    background: var(--c-accent-soft);
    color: var(--c-accent-ink);
    border-color: var(--c-accent-line);
}

.rpt-age-badge--over90 {
    background: var(--c-danger-soft);
    color: var(--c-danger);
    border-color: var(--c-danger-line);
}

/* ---- Print ----------------------------------------------------------------
   A report gets printed. Without this the printed page carried the whole
   application shell, the filters and the buttons, and the grid was clipped to
   whatever the scroll container happened to be showing.
--------------------------------------------------------------------------- */
@media print {
    .app-top,
    .app-header,
    .tnav,
    .nav-scrim,
    .header-actions,
    .filter-controls,
    .rpt-refine,
    .rpt-presets,
    .rpt-breakdown__item,
    .toast-container { display: none !important; }

    /* The chrome is gone from the printed page, so anything that was holding
       the content clear of it has to go too. */
    .app-body { margin-left: 0 !important; }

    .card { border: none !important; box-shadow: none !important; }

    /* Let the whole grid print, not just the visible scroll window. */
    .rpt-table-wrap { overflow: visible !important; }
    .rpt-table-wrap--scroll { max-height: none !important; overflow: visible !important; }
    .rpt-scrollbox { max-height: none !important; overflow: visible !important; }
    .rpt-table-wrap.is-wide .rpt-tbl { min-width: 0 !important; }

    .rpt-tbl { font-size: 10px; }
    .rpt-tbl thead th { position: static; }
    .rpt-tbl__foot td { position: static; }

    /* Repeat the column headers on every printed page. */
    .rpt-tbl thead { display: table-header-group; }
    .rpt-tbl tbody tr { page-break-inside: avoid; }

    .rpt-totals { break-inside: avoid; }
}

/* ---- 14. Report pager ------------------------------------------------------
   Shared by every report through ReportPager.razor. Lives here rather than in
   that component's own <style> block for the reason given at the top of
   section 12: a <style> inside a .razor exists only while that component is
   rendered, so the rules would come and go as the user moved between reports.
   -------------------------------------------------------------------------- */

.rpt-pager,
.app-pager {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 8px 12px;
    border-top: 1px solid var(--c-line);
    background: var(--c-surface-2);
}

.rpt-pager__size,
.app-pager__size {
    display: flex;
    align-items: center;
    gap: 6px;
}

.rpt-pager__size label,
.app-pager__size label {
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--c-ink-3);
    margin: 0;
}

.rpt-pager__size select,
.app-pager__size select {
    width: auto;
    min-width: 72px;
    height: 30px;
    padding: 2px 26px 2px 8px;
    font-size: var(--fs-sm);
}

.rpt-pager__count,
.app-pager__count {
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
    white-space: nowrap;
}

.rpt-pager__count strong,
.app-pager__count strong { color: var(--c-ink); }

.rpt-pager__nav,
.app-pager__nav {
    display: flex;
    align-items: center;
    gap: 3px;
    margin-left: auto;
}

.rpt-pager__btn,
.app-pager__btn {
    min-width: 30px;
    height: 30px;
    padding: 0 7px;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-ink-2);
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-sm, 6px);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.rpt-pager__btn:hover:not(:disabled),
.app-pager__btn:hover:not(:disabled) {
    background: var(--c-surface-2);
    border-color: var(--c-line-strong);
    color: var(--c-ink);
}

.rpt-pager__btn.is-current,
.app-pager__btn.is-current {
    background: var(--c-primary);
    border-color: var(--c-primary);
    color: #fff;
}

.rpt-pager__btn:disabled,
.app-pager__btn:disabled {
    opacity: 0.45;
    cursor: default;
}

.rpt-pager__gap,
.app-pager__gap {
    padding: 0 2px;
    color: var(--c-ink-4, #b9a9ae);
    font-size: var(--fs-sm);
}

@media (max-width: 640px) {
    .rpt-pager__nav,
    .app-pager__nav { margin-left: 0; width: 100%; justify-content: center; }
}


/* Above the grid: a top rule as well, so it reads as the head of the table
   rather than a stray strip, and no rule doubling up under the grid. */
.rpt-pager--top {
    border-top: 0;
    border-bottom: 1px solid var(--c-line);
}
@media print {
    .rpt-pager,
    .app-pager { display: none !important; }
}


/* ---- 14. The common list screen -------------------------------------------
   AppListView / AppPager. Section 6b already gave every list the same two
   body layouts (cards and dense grid rows); this adds the frame around them -
   toolbar, pager, and the two states a list spends a surprising amount of its
   life in.

   The pager itself is not defined here: it shares section 13's rules, so the
   control at the foot of a setup list and the one at the foot of the billing
   report are literally the same CSS rather than two that drifted apart.
--------------------------------------------------------------------------- */

.app-list {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* In a report the pager sits on the card's bottom edge and needs no rounding.
   On a list screen it is a free-standing strip below the rows, so it gets the
   card's own border treatment rather than looking like a detached bar. */
.app-list .app-pager {
    margin-top: var(--s-3);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
}

/* ---- Toolbar additions -------------------------------------------------- */

/* The search box grows an icon. It is the control users look for first on a
   long list, and an unadorned input reads as just another field. */
.list-toolbar__searchbox {
    position: relative;
    display: flex;
    align-items: center;
    min-width: 0;
}

.list-toolbar__searchbox > i {
    position: absolute;
    left: 9px;
    font-size: var(--fs-xs);
    color: var(--c-ink-4);
    pointer-events: none;
}

.list-toolbar__searchbox .list-toolbar__search { padding-left: 27px; }

/* Clear the browser's own "x" - the component owns clearing (Escape, and the
   button in the empty state), and two of them side by side is clutter. */
.list-toolbar__search::-webkit-search-cancel-button { display: none; }

.list-toolbar__filters,
.list-toolbar__actions {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    flex-wrap: wrap;
    min-width: 0;
}

/* ---- Sort picker --------------------------------------------------------
   Components/Common/Pages/SortPicker.razor, the replacement for the clickable
   column headings RadzenDataGrid used to supply. Lives in the same toolbar
   slot as FilterChips and is sized to match it, so a toolbar carrying both
   reads as one strip of controls rather than two. */

.sort-picker {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    min-width: 0;
}

.sort-picker__label {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    margin: 0;
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--c-ink-3);
    white-space: nowrap;
}

.sort-picker__label i { font-size: 0.95em; color: var(--c-ink-4); }

/* Wide enough for the longest option so the control does not resize as the
   choice changes, capped so it cannot crowd out the search box. */
.sort-picker__select {
    width: auto;
    min-width: 11ch;
    max-width: 22ch;
}

.sort-picker__dir {
    flex: 0 0 auto;
    width: 32px;
    padding-inline: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

@media (max-width: 767.98px) {
    .sort-picker { width: 100%; }
    .sort-picker__select { flex: 1 1 auto; max-width: none; }
}

/* A column-header strip for the lists that render true table rows. */
.app-list__head {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-2) var(--s-3);
    background: var(--c-surface-2);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md) var(--r-md) 0 0;
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--c-ink-3);
}

/* ---- Loading and empty -------------------------------------------------- */

/* One block serves both. They are the same shape - an icon, a line of text,
   sometimes an action - and giving them one class is what stops a screen from
   quietly rendering nothing at all, which is what most of these pages did. */
.app-list__state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: var(--s-1);
    padding: var(--s-10) var(--s-4);
    background: var(--c-surface);
    border: 1px dashed var(--c-line-strong);
    border-radius: var(--r-md);
    color: var(--c-ink-3);
}

.app-list__state-icon {
    font-size: 2.25rem;
    line-height: 1;
    color: var(--c-line-strong);
    margin-bottom: var(--s-2);
}

.app-list__state-title {
    margin: 0;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    color: var(--c-ink-2);
}

.app-list__state-hint {
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
    max-width: 46ch;
}

/* ---- Responsive --------------------------------------------------------- */

@media (max-width: 767.98px) {
    /* The toolbar stops being one line: title and count stay together, and
       search, filters and actions each take a full row rather than being
       squeezed to unusable widths. */
    .list-toolbar__searchbox,
    .list-toolbar__filters,
    .list-toolbar__actions { width: 100%; }

    .list-toolbar__actions { justify-content: flex-end; }

    .app-list__state { padding: var(--s-8) var(--s-3); }
}


/* ---- 15. Summary report ---------------------------------------------------
   The operations summary reuses section 12's toolbar, section 13's grid and
   tiles, and the shared ReportPager, so it needs almost nothing of its own.

   This is the exception: while one section is fetching a page the rest of the
   screen is still valid, so the table dims and stops taking clicks rather than
   being replaced by a spinner that throws away the reader's place.
--------------------------------------------------------------------------- */

.rpt-table-wrap.is-busy {
    opacity: 0.55;
    pointer-events: none;
    transition: opacity var(--transition, 140ms ease);
}


/* ---- 16. Keyboard shortcut help -------------------------------------------
   The F1 reference. Every shortcut in this application was bound and none of
   them were written down anywhere a user could find them.
--------------------------------------------------------------------------- */

.sc-overlay {
    position: fixed;
    inset: 0;
    background: color-mix(in srgb, var(--auth-shade) 45%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--s-4);
    z-index: var(--z-modal);
}

.sc-dialog {
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-lg);
    width: min(860px, 100%);
    max-height: min(86vh, 900px);
    display: flex;
    flex-direction: column;
    /* Only the body scrolls - a dialog whose header scrolls away loses the
       close button exactly when a user is looking for it. */
    overflow: hidden;
}

.sc-dialog__head,
.sc-dialog__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
    flex: 0 0 auto;
}

.sc-dialog__head { border-bottom: 1px solid var(--c-line); }

.sc-dialog__foot {
    border-top: 1px solid var(--c-line);
    background: var(--c-surface-2);
}

.sc-dialog__body {
    padding: var(--s-4);
    overflow-y: auto;
    flex: 1 1 auto;
}

.sc-group + .sc-group { margin-top: var(--s-5); }

.sc-group__title {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: var(--fw-semibold);
    color: var(--c-ink-3);
    margin: 0 0 var(--s-2);
}

.sc-table { font-size: var(--fs-sm); margin: 0; }
.sc-table thead th {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-3);
    font-weight: var(--fw-semibold);
    background: var(--c-surface-2);
    border-bottom: 1px solid var(--c-line);
}

.sc-table td { vertical-align: middle; }
.sc-action { font-weight: var(--fw-medium); color: var(--c-ink); white-space: nowrap; }
.sc-desc { color: var(--c-ink-2); }

/* A key looks like a key. Users scan this column for a shape, not a word. */
.sc-key {
    display: inline-block;
    min-width: 26px;
    padding: 2px 7px;
    font-family: var(--f-mono);
    font-size: var(--fs-xs);
    line-height: 1.5;
    text-align: center;
    color: var(--c-ink);
    background: var(--c-surface-2);
    border: 1px solid var(--c-line-strong);
    border-bottom-width: 2px;
    border-radius: var(--r-sm);
    white-space: nowrap;
}

.sc-plus { margin: 0 3px; color: var(--c-ink-3); font-size: var(--fs-xs); }

.sc-foot-note { font-size: var(--fs-sm); color: var(--c-ink-3); }

/* ---- The hint strip on the checkout dialog ---- */

.sc-hints {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-1) var(--s-3);
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.sc-hints__item { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.sc-hints .sc-key { min-width: 0; padding: 1px 5px; }

@media (max-width: 575.98px) {
    .sc-table thead { display: none; }
    .sc-table td { display: block; border: 0; padding: 2px 0; }
    .sc-table tbody tr {
        display: block;
        padding: var(--s-2) 0;
        border-bottom: 1px solid var(--c-line);
    }
    /* On a phone the hint strip is noise next to the fields it describes -
       and there is no keyboard to press the keys on. */
    .sc-hints { display: none; }
}


/* ---- 17. Dashboard ---------------------------------------------------------
   SuperAdminDashboard.razor was written against a stylesheet that does not
   exist: 77 of the 81 class names it uses are defined nowhere, and its only
   <style> block is a `spin` keyframe. The page therefore rendered as raw
   block elements.

   The visible symptom was the icons. Every icon on that page is an inline
   <svg> with a viewBox and no width, height or CSS size - and an SVG with no
   intrinsic size fills its container. With no rule to constrain them, all 22
   grew to the full 1270px content width, one under another, and the landing
   page came out 24,041 pixels tall with the actual figures scattered between
   them. That is why the dashboard could not be read at all.

   Sized from the same tokens as the rest of the application, so the dashboard
   matches the screens around it rather than being a second visual language.
   Lives here, not in a <style> block on the page, for the reason given at the
   top of section 12.
   -------------------------------------------------------------------------- */

/* Quick actions beside the figures beside the analytics rail. Collapses before
   the middle column gets too narrow to hold a stat card. */
.main-container {
    display: grid;
    grid-template-columns: minmax(0, 250px) minmax(0, 1fr) minmax(0, 280px);
    gap: var(--s-4);
    align-items: start;
    padding: var(--s-4);
}

.left-sidebar,
.right-sidebar {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    min-width: 0;
}

.content {
    display: flex;
    flex-direction: column;
    gap: var(--s-4);
    min-width: 0;
}

.sidebar-title,
.analytics-title {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-ink-3);
    margin: 0 0 var(--s-1);
}

/* THE fix: every dashboard icon is a bare <svg> with no size. Stated once for
   the page rather than per card; anything that wants to be bigger asks below. */
.main-container svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    stroke-width: 2;
}

/* ── Cards ── */
.quick-card,
.stat-card,
.chart-card,
.mini-stat,
.financial-card,
.analytics-card,
.popular-items {
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    padding: var(--s-3);
    min-width: 0;
}

.quick-card-header,
.chart-header,
.financial-header,
.popular-header {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    margin-bottom: var(--s-2);
}

.chart-header {
    justify-content: space-between;
    align-items: flex-start;
}

.quick-card-title,
.financial-title,
.popular-title,
.chart-title {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
}

.quick-card-icon,
.financial-header > svg,
.popular-header > svg {
    color: var(--c-ink-3);
}

/* ── Key/value rows: table status, orders, staff, inventory, popular items ── */
.status-row,
.order-item,
.staff-item,
.financial-row,
.financial-item,
.popular-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    padding: 5px 0;
    border-top: 1px solid var(--c-line-soft);
    font-size: var(--fs-sm);
}

.status-row:first-of-type,
.order-item:first-of-type,
.staff-item:first-of-type,
.popular-item:first-of-type {
    border-top: none;
}

.status-label,
.order-detail,
.popular-count,
.bar-label,
.analytics-sub {
    color: var(--c-ink-3);
    font-size: var(--fs-xs);
}

.status-value,
.order-id,
.popular-name,
.financial-total {
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
}

/* Colour words used as modifiers throughout the dashboard markup. */
.main-container .green { color: var(--c-success); }
.main-container .blue { color: var(--c-info); }
.main-container .orange { color: var(--c-warning); }
.main-container .red { color: var(--c-danger); }
/* The four "colour name" helper classes below are legacy: pages ask for
   .green/.blue/.orange/.purple by name. They now all resolve inside the two
   brand hues, so a page that asks for purple gets the quiet brand tone rather
   than a third colour. Renaming the classes would mean touching every page
   that uses them; redefining them is one edit and the same result. */
.main-container .purple { color: var(--c-info); }

/* ── Staff ── */
.staff-item { justify-content: flex-start; }

.staff-avatar {
    width: 28px;
    height: 28px;
    border-radius: var(--r-full);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: #fff;
    background: var(--c-ink-3);
    flex: 0 0 auto;
}

.staff-avatar.blue { background: var(--c-info); color: #fff; }
.staff-avatar.green { background: var(--c-success); color: #fff; }
.staff-avatar.purple { background: var(--c-info); color: #fff; }

.staff-info .name {
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--c-ink);
}

.staff-info .role {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

/* ── Stat cards ── */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--s-3);
}

.stat-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--s-2);
}

.stat-icon,
.mini-stat-icon {
    width: 32px;
    height: 32px;
    border-radius: var(--r-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--c-surface-2);
    color: var(--c-ink-2);
    flex: 0 0 auto;
}

.stat-icon.green, .mini-stat-icon.green { background: var(--c-success-soft); color: var(--c-success); }
.stat-icon.blue, .mini-stat-icon.blue { background: var(--c-info-soft); color: var(--c-info); }
.stat-icon.orange, .mini-stat-icon.orange { background: var(--c-warning-soft); color: var(--c-warning); }
.stat-icon.purple, .mini-stat-icon.purple { background: var(--c-info-soft); color: var(--c-info); }

.stat-trend,
.chart-trend {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    padding: 2px 6px;
    border-radius: var(--r-full);
    background: var(--c-success-soft);
    color: var(--c-success);
    white-space: nowrap;
}

.stat-trend.down,
.stat-trend.negative {
    background: var(--c-danger-soft);
    color: var(--c-danger);
}

.stat-value {
    font-size: var(--fs-xl);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    line-height: var(--lh-tight);
}

.stat-label {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
    margin-top: 2px;
}

/* The sparkline is the one SVG that should stretch - it is a chart, not an
   icon, so it opts out of the sizing rule above. */
.stat-chart {
    margin-top: var(--s-2);
    height: 30px;
}

.stat-chart svg,
.mini-line-chart svg {
    width: 100%;
    height: 100%;
}

/* ── Charts ── */
.charts-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--s-3);
}

.chart-value {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
}

.bar-chart {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--s-2);
    height: 90px;
    padding-top: var(--s-2);
}

.bar-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    flex: 1;
    min-width: 0;
}

.bar {
    width: 100%;
    max-width: 26px;
    background: var(--c-primary);
    border-radius: var(--r-sm) var(--r-sm) 0 0;
    opacity: 0.85;
}

.customer-flow-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.mini-line-chart {
    height: 40px;
    margin-top: var(--s-2);
}

/* ── Bottom strip ── */
.bottom-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: var(--s-3);
}

.mini-stat {
    display: flex;
    align-items: center;
    gap: var(--s-2);
}

.mini-stat-info .label {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.mini-stat-info .value {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
}

/* ── Financial / inventory ── */
.financial-grid {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
}

.inventory-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s-2);
    margin-top: var(--s-2);
}

.inventory-item {
    text-align: center;
    padding: var(--s-2) 4px;
    background: var(--c-surface-2);
    border-radius: var(--r-sm);
}

.inventory-item .label {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.inventory-item .value {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
}

/* ── Analytics rail ── */
.analytics-label {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.analytics-value {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
}

.view-report {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--c-primary);
    text-decoration: none;
}

.view-report:hover { text-decoration: underline; }

.live-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: var(--s-2);
    flex-wrap: wrap;
}

.live-tab {
    font-size: var(--fs-xs);
    padding: 3px 8px;
    border-radius: var(--r-full);
    border: 1px solid var(--c-line);
    background: var(--c-surface);
    color: var(--c-ink-3);
    cursor: pointer;
}

.live-tab.active {
    background: var(--c-primary);
    border-color: var(--c-primary);
    color: var(--c-on-primary);
}

/* ── Responsive ──
   The rails drop below the figures rather than being squeezed; the figures are
   what the page is for. */
@media (max-width: 1200px) {
    .main-container { grid-template-columns: minmax(0, 220px) minmax(0, 1fr); }

    .right-sidebar {
        grid-column: 1 / -1;
        flex-direction: row;
        flex-wrap: wrap;
    }

    .right-sidebar > * { flex: 1 1 220px; }
}

@media (max-width: 860px) {
    .main-container {
        grid-template-columns: minmax(0, 1fr);
        padding: var(--s-3);
    }

    .left-sidebar,
    .right-sidebar { flex-direction: column; }

    .right-sidebar > * { flex: 1 1 auto; }
}


/* ---- 17b. Owner dashboard, reorganised (2026-09-19) ----------------------
   The dashboard is now five independently-loading sections (see
   DashboardService), so it needs a loading state, a failed state, the period
   picker, real bar charts and the Team (HR) tiles. All colour comes from the
   tokens above; the only hex values left are the categorical chart colours,
   which are data colours, not chrome (see the design-system notes).
   -------------------------------------------------------------------------- */

/* ── Period picker ── */
.dash-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-3) var(--s-4);
    padding: var(--s-3) var(--s-4);
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-line);
}

.dash-presets,
.dash-range,
.dash-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

.dash-range .form-control { width: auto; }
.dash-range-sep { color: var(--c-ink-3); font-size: var(--fs-sm); }

/* The comparison label sits at the end of the bar, pushed right when there is room. */
.dash-meta {
    margin-left: auto;
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
}

.dash-meta strong { color: var(--c-ink); font-weight: var(--fw-semibold); }

.dash-chip {
    font: inherit;
    font-size: var(--fs-sm);
    padding: 5px 12px;
    border-radius: var(--r-full);
    border: 1px solid var(--c-line);
    background: var(--c-surface);
    color: var(--c-ink-2);
    cursor: pointer;
}

.dash-chip:hover { background: var(--c-surface-2); }
.dash-chip:focus-visible { outline: 2px solid var(--c-ring); outline-offset: 1px; }

.dash-chip.active {
    background: var(--c-primary);
    border-color: var(--c-primary);
    color: var(--c-on-primary);
}

/* ── Card states ── */
.dash-skel {
    border-radius: var(--r-md);
    background: linear-gradient(90deg, var(--c-surface-2) 25%, var(--c-line-soft) 50%, var(--c-surface-2) 75%);
    background-size: 200% 100%;
    animation: dash-shimmer 1.3s linear infinite;
}

@keyframes dash-shimmer {
    from { background-position: 200% 0; }
    to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .dash-skel { animation: none; }
}

.dash-error {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    padding: var(--s-3);
    border: 1px solid var(--c-danger-line);
    border-radius: var(--r-md);
    background: var(--c-danger-soft);
    color: var(--c-danger);
    font-size: var(--fs-sm);
}

.dash-retry {
    font: inherit;
    font-size: var(--fs-xs);
    padding: 3px 10px;
    border-radius: var(--r-full);
    border: 1px solid var(--c-danger-line);
    background: var(--c-surface);
    color: var(--c-danger);
    cursor: pointer;
}

.dash-empty {
    padding: var(--s-3) 0;
    font-size: var(--fs-sm);
    color: var(--c-ink-3);
}

.dash-hint,
.dash-sub {
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.dash-hint { padding-top: var(--s-1); }
.dash-of { font-size: var(--fs-sm); font-weight: var(--fw-normal); color: var(--c-ink-3); }
.dash-warn { color: var(--c-warning); font-weight: var(--fw-medium); }
.dash-count { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--c-ink-2); }

.stat-sub { margin-top: 2px; font-size: var(--fs-xs); color: var(--c-ink-3); }

/* A comparison that is neither good nor bad (purchases), or has nothing to compare with. */
.stat-trend.flat { background: var(--c-surface-2); color: var(--c-ink-3); }

/* ── Charts: real data, so the bars carry state ── */
.dash-split {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: var(--s-3);
}

.bar-chart--tall { height: 150px; }

/* A 90-day trend has to fit; the gap that suits seven bars would not. */
.bar-chart--dense { gap: 2px; }
.bar-chart--dense .bar { max-width: none; }

.bar.is-zero { opacity: 0.25; }
.bar.is-peak { background: var(--c-primary); opacity: 1; }
.bar-chart:not(.bar-chart--tall) .bar:not(.is-peak) { background: var(--c-line-strong); }

.bar-label { white-space: nowrap; min-height: 1em; }

/* ── Share bars: table occupancy and payment methods ──
   Five slots, used in order, so the same method does not change colour
   between two loads. Categorical colours, deliberately not the brand rose. */
.share-bar {
    display: flex;
    height: 8px;
    margin: var(--s-2) 0;
    border-radius: var(--r-full);
    overflow: hidden;
    background: var(--c-surface-2);
}

.share-bar--tall { height: 12px; margin-bottom: var(--s-3); }

/* Share-bar segments. Same decision as the dashboard's .odb-series-* ramp:
   these were a rose, a blue, a green, an amber and a grey, and the blue and
   the green were two of the three hues this product does not use. They are
   now five steps from the rose to the gold, separated by lightness as well as
   by hue so they still read apart in greyscale and for colour-vision
   deficiency. Fixed order - a legend must not move. */
.seg-1 { background: var(--auth-primary, #a8394a); }
.seg-2 { background: color-mix(in srgb, var(--auth-primary) 62%, var(--auth-accent)); }
.seg-3 { background: color-mix(in srgb, var(--auth-accent) 70%, var(--auth-shade)); }
.seg-4 { background: var(--auth-accent, #d6a12c); }
.seg-5 { background: color-mix(in srgb, var(--auth-accent) 40%, var(--auth-surface)); }

.legend-swatch {
    display: inline-block;
    width: 9px;
    height: 9px;
    margin-right: 6px;
    border-radius: 2px;
}

/* ── Ranked list: top items ── */
.rank-row { padding: 6px 0; }

.rank-top {
    display: flex;
    justify-content: space-between;
    gap: var(--s-2);
    font-size: var(--fs-sm);
}

.rank-track {
    height: 4px;
    margin-top: 4px;
    border-radius: var(--r-full);
    background: var(--c-surface-2);
}

.rank-track > span {
    display: block;
    height: 100%;
    border-radius: var(--r-full);
    background: var(--c-primary);
}

/* ── Team (HR) ──
   `is-pending` is a tile whose source does not exist yet. It reads as
   "not switched on" (dashed, muted), never as a real zero. */
.hr-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: var(--s-3);
}

.hr-tile {
    padding: var(--s-3);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    background: var(--c-surface);
    min-width: 0;
}

.hr-tile--wide { grid-column: span 2; }

.hr-tile.is-pending {
    border-style: dashed;
    background: var(--c-surface-2);
}

.hr-tile.is-pending .hr-tile-value { color: var(--c-ink-4); }

.hr-tile-label { font-size: var(--fs-xs); color: var(--c-ink-3); }

.hr-tile-value {
    font-size: var(--fs-xl);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    line-height: var(--lh-tight);
}

.hr-tile-value.sm { display: block; font-size: var(--fs-lg); }

.hr-sub { min-height: 1em; margin-top: 2px; font-size: var(--fs-xs); color: var(--c-ink-3); }

.hr-tag {
    display: inline-block;
    margin-top: var(--s-2);
    padding: 1px 8px;
    border-radius: var(--r-full);
    background: var(--c-line-soft);
    font-size: var(--fs-xs);
    color: var(--c-ink-3);
}

.hr-quad {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--s-2);
    margin-top: 2px;
}

.hr-quad-label { display: block; font-size: var(--fs-xs); color: var(--c-ink-3); }

.hr-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: var(--s-2); }

.hr-chip {
    padding: 1px 8px;
    border-radius: var(--r-full);
    background: var(--c-surface-2);
    font-size: var(--fs-xs);
    color: var(--c-ink-2);
}

@media (max-width: 860px) {
    .dash-split { grid-template-columns: minmax(0, 1fr); }
    .hr-tile--wide { grid-column: auto; }
    .dash-meta { margin-left: 0; }
}


/* ---- 17. Custom report builder --------------------------------------------
   The configuration panel. Five steps laid out as cards so the whole setup is
   visible at once rather than behind a wizard - a report builder is something
   people adjust and re-preview repeatedly, and a wizard makes every small
   change a walk back through four screens.
--------------------------------------------------------------------------- */

.cr-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
    gap: var(--s-3);
    align-items: start;
}

/* The save row spans the full width: it is the end of the sequence, not one
   more option among four. */
.cr-panel--wide { grid-column: 1 / -1; }

.cr-panel {
    background: var(--c-surface);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    padding: var(--s-3);
    min-width: 0;
}

.cr-panel__title {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--c-ink);
    margin: 0 0 var(--s-3);
}

.cr-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    border-radius: 50%;
    background: var(--c-primary);
    color: var(--c-on-primary);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
}

/* ---- Field picker ---- */

.cr-fields {
    max-height: 260px;
    overflow-y: auto;
    border: 1px solid var(--c-line);
    border-radius: var(--r-sm);
    padding: var(--s-2);
}

.cr-fieldgroup + .cr-fieldgroup { margin-top: var(--s-3); }

.cr-fieldgroup__title {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: var(--fw-semibold);
    color: var(--c-ink-3);
    margin-bottom: 2px;
}

.cr-field {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: 3px 4px;
    border-radius: var(--r-sm);
    font-size: var(--fs-sm);
    cursor: pointer;
}

.cr-field:hover { background: var(--c-surface-2); }
.cr-field.is-on { background: var(--c-accent-soft); }

/* ---- Chosen columns, in order ---- */

.cr-chosen {
    margin-top: var(--s-3);
    border: 1px solid var(--c-line);
    border-radius: var(--r-sm);
    padding: var(--s-2);
}

.cr-chosen__title {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    font-weight: var(--fw-semibold);
    color: var(--c-ink-3);
    margin-bottom: var(--s-1);
}

.cr-chosen__row {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: 2px 0;
    font-size: var(--fs-sm);
}

.cr-chosen__row > span { flex: 1 1 auto; min-width: 0; }
.cr-chosen__btns { display: flex; gap: 2px; flex: 0 0 auto; }
.cr-chosen__btns .btn { padding: 1px 6px; }

/* ---- Filter / sort / aggregate rows ---- */

.cr-filter {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    margin-bottom: var(--s-2);
    flex-wrap: wrap;
}

.cr-filter > select,
.cr-filter > input { flex: 1 1 110px; min-width: 0; }
.cr-filter > .btn { flex: 0 0 auto; }

.cr-share {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--fs-sm);
    white-space: nowrap;
    cursor: pointer;
}

@media (max-width: 575.98px) {
    .cr-grid { grid-template-columns: 1fr; }
    .cr-filter > select,
    .cr-filter > input { flex-basis: 100%; }
}


/* ═══════════════════════════════════════════════════════════════════
   12. STICKY COLUMN HEADINGS
   ───────────────────────────────────────────────────────────────────
   A hundred rows of numbers with the labels scrolled off the top is a
   screen nobody can read. The billing report solved this for itself
   (section 6c, .rpt-tbl) and nothing else did, so every other list -
   users, customers, items, stock, seed catalog, the host screens -
   still lost its headings on the way down.

   Doing it once, here, for every table in the product.

   The awkward part is what "top" means. position: sticky measures
   against the nearest scrolling ancestor, and this product has two
   kinds of table:

     * most scroll with the page, and the page has a sticky application
       header 58px tall sitting above them - so their headings have to
       stop at --app-header-h or they slide underneath it and are
       invisible exactly when they are needed;
     * some sit inside a box that scrolls on its own - a dialog body,
       an offcanvas - where the heading must stop at the top of that
       box, and --app-header-h would push it down out of view.

   The default is the first case, and the boxes that scroll on their
   own are named below. A page with its own scroll container can join
   them by adding .is-scrollbox.
   ═══════════════════════════════════════════════════════════════════ */

.table > thead > tr > th,
.rpt-tbl thead th,
.seed-table thead th,
.host-table thead th,
.sc-table thead th,
.ss-table thead th,
.vp-table thead th,
.dd-table thead th,
.split-table thead th {
    position: sticky;
    top: var(--app-header-h, 88px);
    z-index: var(--z-sticky, 100);

    /* Sticky cells are painted over the rows sliding beneath them, so a
       transparent heading shows the data through it. Bootstrap's .table
       leaves th transparent by default. */
    background-color: var(--c-surface-2, #f6f2ec);

    /* border-bottom on a sticky cell scrolls away with the table in
       collapsed-border tables; a shadow is painted with the cell and
       stays put. */
    box-shadow: inset 0 -1px 0 var(--c-line, #e3dcd2);
}

/* Inside a box with its own scrollbar, the heading belongs to that box.
   These are the scroll containers this product actually has - grep for
   "overflow-y: auto" if you add another, and put it here too. */
.modal-body-custom .table > thead > tr > th,
.modal-body .table > thead > tr > th,
.offcanvas-body .table > thead > tr > th,
.sc-dialog__body .table > thead > tr > th,
.sc-dialog__body .sc-table thead th,
.tbl-act-dialog .table > thead > tr > th,
.rpt-table-wrap--scroll .rpt-tbl thead th,
.rpt-scrollbox .table > thead > tr > th,
.rpt-scrollbox .rpt-tbl thead th,
.co-body .table > thead > tr > th,
/* Scroll boxes declared in a component's own <style> block rather than
   here: split billing, and the KDS panels. */
.split-table-wrap .table > thead > tr > th,
.split-table-wrap .split-table thead th,
.split-checkout-body .table > thead > tr > th,
.panel-body .table > thead > tr > th,
/* The owner dashboard's drill-down dialog and its branch table. Their own
   .odb-table headings are pinned in dashboard.css, which loads later; these
   two cover a plain Bootstrap .table dropped inside either box. */
.odb-modal__body .table > thead > tr > th,
.odb-tablewrap .table > thead > tr > th,
/* The host admin area's table wrapper. It declares only overflow-x: auto,
   which is easy to read as "not a vertical scroll box" - but CSS computes
   overflow-y: visible to auto as soon as the other axis is not visible, so it
   IS one, and the heading was being pinned 58px down INSIDE it. That renders
   as the column headings floating below the first row. */
.host-table-wrap .table > thead > tr > th,
.host-table-wrap .host-table thead th,
/* The stock module's panel and the location-pricing table wrapper. Same trap
   as .host-table-wrap above, and both were live:

     .stock-panel declares only overflow-x: auto, so overflow-y computes to
     auto and it IS a scroll container - but it is always exactly as tall as
     its content, so it never scrolls. The heading was therefore pinned 88px
     down INSIDE a box that does not move, where it sat ON TOP of the fourth
     row of Quick Setup's table and hid it.

     .lp-table-wrap has a real max-height, so its heading was pinned 88px below
     the top of a box that does scroll - it floated in the middle of the table
     with a row above it.

   Both now stop at their own box's top, and both pages give that box a bounded
   height so the heading actually stays put while the rows move. */
.stock-panel .table > thead > tr > th,
.stock-panel .seed-table thead th,
.stock-panel .line-editor thead th,
.lp-table-wrap .table > thead > tr > th,
.lp-table-wrap .lp-table thead th,
.is-scrollbox .table > thead > tr > th,
.is-scrollbox .rpt-tbl thead th,
.is-scrollbox .seed-table thead th,
.is-scrollbox .host-table thead th,
.is-scrollbox .sc-table thead th,
.is-scrollbox .ss-table thead th,
.is-scrollbox .vp-table thead th,
.is-scrollbox .dd-table thead th,
.is-scrollbox .split-table thead th {
    top: 0;
}

/* Bootstrap's .table-responsive sets overflow-x: auto, which makes it a
   scroll container too - so a sticky heading inside it is measured against
   the wrapper, not the page, and top: 58px pushes the heading 58px DOWN on
   top of the first rows, where it is hidden behind them. Inside that wrapper
   the heading can only ever sit at the wrapper's own top. */
.table-responsive .table > thead > tr > th,
.table-responsive .rpt-tbl thead th,
.table-responsive .sc-table thead th {
    top: 0;
}

/* Two heading rows (a group row above the column row) stack rather than
   overlapping, so both stay readable. Needs the row heights to be known,
   which is why it is opt-in per table. */
.tbl-head-2 thead tr:first-child th { top: var(--app-header-h, 88px); }
.tbl-head-2 thead tr:nth-child(2) th { top: calc(var(--app-header-h, 88px) + 34px); }

/* Escape hatch: a short table inside a card, a receipt preview, or any
   layout where a pinned heading would be wrong. */
.no-sticky-head thead th,
.no-sticky-head > thead > tr > th {
    position: static;
    box-shadow: none;
}

@media print {
    /* Sticky positioning splits headings across printed pages oddly, and
       print already repeats thead on each page by itself. */
    .table > thead > tr > th,
    .rpt-tbl thead th,
    .seed-table thead th,
    .host-table thead th,
    .sc-table thead th,
    .ss-table thead th,
    .vp-table thead th,
    .dd-table thead th,
    .split-table thead th {
        position: static;
        box-shadow: none;
    }
}
