﻿/* =============================================================================================
   Web Office — application stylesheet.

   MID-MIGRATION. The SHELL (sidebar, nav, top bar) is on the shared design system and reads
   --c-* / --glass-* from _content/AdvDeli.Catalog.UI/theme.css, which Components/App.razor now
   links AFTER this file so it wins html and body. Everything below the shell section is still on
   the legacy palette declared immediately below.

   DO NOT DELETE THE LEGACY :root BLOCK YET. Around twenty pages — tables, badges, buttons,
   forms, the reference-data dialogs — still style themselves from --navy/--copper/--ink/--line.
   Removing it is the LAST step of the migration; do it early and every one of those pages
   renders with unresolved colours and no error anywhere. The names do not collide with the
   theme's, so the two can coexist for as long as the conversion takes.
   ============================================================================================= */

:root {
    /* LEGACY. Consumed by the not-yet-converted pages only. Nothing in the shell reads these. */
    --navy: #16324f;
    --navy2: #1f4b74;
    --copper: #b5651d;
    --copper2: #d98324;
    --ink: #22303c;
    --muted: #6b7785;
    --line: #e6e9ee;
    --bg: #f4f6f9;

    /* Height of the sticky app bar (.bo-top), and the offset every OTHER sticky element on a page
       has to sit below. It was a bare 78px written into one rule, which is how the report
       document toolbar came to be pinned at top:0 — the same place the app bar already occupies,
       and behind it, since the bar wins on z-index. A toolbar that scrolls out of sight while
       claiming position:sticky is worse than one that never claimed it. */
    --bo-top-h: 61px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }

/* iOS Safari inflates text on pages it judges not to be mobile-shaped, and it does so PER BLOCK
   rather than uniformly — so two controls side by side can end up with different text sizes and
   fall out of alignment. It is one of the few things that genuinely renders differently on a
   handset than in a desktop browser's device emulator, which makes it hard to find by looking.
   Pinning it to 100% says "this page is already laid out for this width, leave it alone". */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* Fragment links (the Item Manager's back-to-top) glide rather than jump — but only for people
   who have not asked their system for less motion. */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}
/* Font, background and text colour now come from theme.css, which is linked after this file.
   The old body rule set Segoe UI on a #f4f6f9 ground; the token values are near-identical greys,
   so what actually changed is the typeface. */
a { color: var(--c-accent-text); text-decoration: none; }
h1, h2, h3 { margin: 0; }
h1:focus { outline: none; }

/* =============================================================================================
   SHELL — converted to the shared design system.

   Was a navy gradient sidebar with a copper logo and a white top bar. It now uses the same glass
   and the same saffron accent as the till, so the two products read as one.

   MOUSE, NOT TOUCH. The Cashier sizes every control to 44px minimum because it runs on a
   touchscreen. This is a browser application; that sizing here just reads as oversized and
   pushes content down the page. Nav links are ~38px, which is right for a pointer.
   ============================================================================================= */

.bo { display: flex; min-height: 100vh; isolation: isolate; }

/* The colour ground, behind everything. Fixed so the content scrolls over it rather than
   dragging it along, and on .bo rather than a page so every screen inherits it. */
.bo::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(62% 58% at 6% 4%, var(--mesh-a), transparent 68%),
        radial-gradient(58% 62% at 95% 14%, var(--mesh-b), transparent 68%),
        radial-gradient(66% 60% at 14% 94%, var(--mesh-c), transparent 70%),
        radial-gradient(60% 56% at 88% 90%, var(--mesh-d), transparent 70%);
}

/* PANE glass: this is a full-height surface, so --glass-fill (.70 white) would flatten the mesh
   behind it. No backdrop-filter either — an element with one becomes a backdrop root and kills
   the blur on anything rendered inside it. */
.bo-side {
    width: 264px; flex: 0 0 264px;
    background: var(--glass-fill-pane);
    border-right: 1px solid var(--glass-line);
    color: var(--c-text);
    display: flex; flex-direction: column;
    position: sticky; top: 0; height: 100vh; overflow-y: auto;
}

.bo-brand { display: flex; align-items: center; gap: 0.75rem; padding: 1.1rem 1.1rem; border-bottom: 1px solid var(--glass-line); }

/* Holds the AE mark, sized by font-size because the mark is 1em tall. Was the letters "AK" on a
   copper gradient, which named nothing. */
.bo-logo {
    width: 42px; height: 42px; flex: 0 0 auto;
    border-radius: var(--r-sm);
    display: grid; place-items: center;
    font-size: 23px;
    color: var(--c-accent-ink);
    background: var(--c-accent);
    box-shadow: 0 6px 18px -6px rgba(245, 165, 36, .45);
}
.bo-title { font-weight: 800; font-size: 1.05rem; letter-spacing: var(--track-display); color: var(--c-text); }
.bo-sub { font-size: 0.74rem; color: var(--c-text-faint); letter-spacing: var(--track-label); text-transform: uppercase; font-weight: 600; }

.bo-nav { padding: 0.5rem 0.6rem 2rem; }
.nav-group {
    display: flex; align-items: center; gap: 0.5rem; padding: 1rem 0.7rem 0.35rem;
    font-size: 0.7rem; font-weight: 700; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint);
}
/* Holds a BoIcon. Sized here rather than in the component so the nav owns its own scale — the
   icons are stroked paths at currentColor, so they take the group heading's colour for free. */
.ng-ic { display: inline-flex; align-items: center; }
.ng-ic .bo-ico { width: 15px; height: 15px; }
.nav-link {
    display: block; padding: 0.55rem 0.75rem; margin: 1px 0;
    border-radius: var(--r-sm);
    color: var(--c-text-dim); font-size: 0.9rem;
    border-left: 3px solid transparent;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: background-color .12s ease, color .12s ease;
}
.nav-link:hover { background: var(--glass-fill-hi); color: var(--c-text); }
.nav-link.active {
    background: var(--c-accent-wash);
    color: var(--c-accent-text);
    border-left-color: var(--c-accent);
    font-weight: 600;
}
.nav-link.dash { margin: 0.5rem 0 0.2rem; font-weight: 600; }

.bo-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* The top bar KEEPS its backdrop-filter. It is the one surface content scrolls underneath, so
   the blur does real work here, and nothing inside it needs a backdrop of its own. */
.bo-top {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    background: var(--glass-fill);
    border-bottom: 1px solid var(--glass-line);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    padding: 0.8rem 1.4rem;
    position: sticky; top: 0; z-index: 10;
    /* Explicit, so --bo-top-h is guaranteed rather than guessed. Its content — a 34px avatar
       inside 0.8rem of padding — already comes to just under this, so nothing moves. */
    min-height: var(--bo-top-h); box-sizing: border-box;
}
.bo-store { display: flex; align-items: center; gap: 0.7rem; font-weight: 700; color: var(--c-text); }
.bo-user { display: flex; align-items: center; gap: 0.6rem; color: var(--c-text-dim); font-size: 0.9rem; }

/* Always in the markup, inert above the drawer breakpoint. Not conditionally rendered, because a
   scrim that enters the DOM at the same moment it becomes visible cannot transition — it would
   snap in rather than fade. */
.bo-scrim { display: none; }

/* The drawer's state lives here — see the note in MainLayout for why a checkbox rather than an
   event handler. Off-screen rather than display:none or hidden, both of which would remove it
   from the tab order and make the menu unreachable by keyboard. */
.bo-nav-cb {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* Focusing the hidden checkbox has to show on its visual stand-in, or a keyboard user tabs onto
   the menu control and sees nothing at all. */
.bo-nav-cb:focus-visible ~ .bo-main .bo-burger {
    outline: 2px solid var(--c-accent, #d98324);
    outline-offset: 2px;
}

/* The drawer toggle. Absent above 860px, where the sidebar is permanently on screen — display
   rather than visibility, so it takes no space in the bar on a desktop. Three spans instead of an
   icon component because the bars animate into a cross, which a glyph cannot do. */
.bo-burger {
    display: none;
    flex-direction: column; justify-content: center; gap: 5px;
    width: 42px; height: 42px; flex: 0 0 auto;
    padding: 0 9px;
    background: transparent; border: 1px solid var(--glass-line-hi); border-radius: var(--r-sm);
    cursor: pointer;
}
.bo-burger span {
    display: block; height: 2px; width: 100%; border-radius: 2px;
    background: var(--c-text);
    transition: transform .2s ease, opacity .2s ease;
}
/* Open state: top and bottom bars cross, middle fades. Tells you the same button closes it. */
.bo-nav-cb:checked ~ .bo-main .bo-burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.bo-nav-cb:checked ~ .bo-main .bo-burger span:nth-child(2) { opacity: 0; }
.bo-nav-cb:checked ~ .bo-main .bo-burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
    .bo-burger span, .bo-side, .bo-scrim { transition: none; }
}

.bo-storepick {
    display: inline-flex; align-items: center; gap: 0.35rem; text-decoration: none;
    border: 1px solid var(--glass-line-hi); border-radius: var(--r-sm);
    padding: 5px 11px; font-size: 0.82rem; font-weight: 700;
    color: var(--c-text); background: var(--c-s2);
}
.bo-storepick:hover { border-color: var(--c-accent); background: var(--c-accent-wash); color: var(--c-accent-text); }
/* Amber rather than red: no store selected is a state to resolve, not a failure. */
.bo-nostore { color: var(--c-warn); font-style: italic; font-weight: 600; }
.bo-caret { font-size: 0.7rem; color: var(--c-text-faint); }

.bo-signout {
    margin-left: 0.5rem; font-weight: 700; font-size: 0.82rem; text-decoration: none;
    color: var(--c-text); background: var(--c-s2);
    border: 1px solid var(--glass-line-hi); border-radius: var(--r-sm);
    padding: 6px 12px;
}
.bo-signout:hover { border-color: var(--c-accent); background: var(--c-accent-wash); color: var(--c-accent-text); }

.bo-avatar {
    width: 34px; height: 34px; flex: 0 0 auto;
    border-radius: 50%;
    background: var(--c-accent); color: var(--c-accent-ink);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 0.8rem;
}
.bo-content { padding: 1.5rem 1.6rem; flex: 1; min-width: 0; }

/* ---- Page header ---- */
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 1.2rem; flex-wrap: wrap; }
.page-head h1 { font-size: 1.55rem; font-weight: 800; color: var(--navy); }
.page-head .sub { color: var(--muted); margin-top: 0.2rem; font-size: 0.95rem; }
.page-actions { display: flex; gap: 0.6rem; }

/* ---- Cards ---- */
.card { background: #fff; border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 1px 3px rgba(20,40,70,0.05); }
.card-pad { padding: 1.2rem 1.3rem; }

/* KPI tiles */
.kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.4rem; }
.kpi { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.1rem 1.2rem; box-shadow: 0 1px 3px rgba(20,40,70,0.05); }
.kpi .k-label { color: var(--muted); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.4px; }
.kpi .k-value { font-size: 1.8rem; font-weight: 800; color: var(--navy); margin-top: 0.3rem; }
.kpi .k-foot { color: var(--muted); font-size: 0.82rem; margin-top: 0.2rem; }
.kpi.copper { border-left: 4px solid var(--copper2); }
.kpi.navy { border-left: 4px solid var(--navy2); }
.kpi.green { border-left: 4px solid #2f9e44; }
.kpi.violet { border-left: 4px solid #7048e8; }

/* ---- Toolbar ---- */
.toolbar { display: flex; gap: 0.7rem; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; }
.search { position: relative; flex: 1; min-width: 200px; max-width: 380px; }
.search input { width: 100%; height: 2.6rem; border: 1px solid var(--line); border-radius: 9px; padding: 0 0.9rem; font-size: 0.95rem; outline: none; }
.search input:focus { border-color: var(--copper2); box-shadow: 0 0 0 3px rgba(217,131,36,0.14); }
.muted-count { color: var(--muted); font-size: 0.9rem; }

/* ---- Tables ---- */
.grid-wrap { background: #fff; border: 1px solid var(--line); border-radius: 14px; overflow: auto; }
table.grid { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
table.grid thead th { text-align: left; background: #f7f9fb; color: var(--muted); font-weight: 700;
    font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.4px; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
table.grid tbody td { padding: 0.7rem 0.9rem; border-bottom: 1px solid #eef1f5; }
/* One line per cell, ellipsed past a sane width.
   inline-block with a max-width is what makes this work under the table's AUTO layout: it gives
   the cell a definite maximum, so a long value can no longer dictate the column width. Doing it
   with table-layout: fixed instead would re-size every column on every grid in the Back Office.
   Keeping it to one line also stops a multi-value cell (a list of roles) from making its row
   several times taller than its neighbours. */
table.grid tbody td .cell-txt { display: inline-block; max-width: 32ch; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
table.grid tbody tr:hover { background: #fafbfd; }
table.grid tbody tr:last-child td { border-bottom: none; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: 'Consolas', monospace; }

/* grid toolbar */
.grid-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap; }
.gt-left { display: flex; gap: 0.6rem; }

/* sortable headers */
table.grid thead th.sortable { cursor: pointer; user-select: none; }
table.grid thead th.sortable:hover { color: var(--copper); }
table.grid thead th .arw { color: var(--copper2); font-size: 0.7rem; }
table.grid thead th.act { width: 1%; text-align: center; }

/* per-column filter row */
tr.filter-row th { padding: 0.35rem 0.5rem 0.5rem; background: #f7f9fb; border-bottom: 1px solid var(--line); }
tr.filter-row input { width: 100%; min-width: 70px; height: 1.9rem; border: 1px solid var(--line); border-radius: 6px;
    padding: 0 0.45rem; font-size: 0.8rem; font-weight: 400; outline: none; }
tr.filter-row input:focus { border-color: var(--copper2); }
td.act { text-align: center; white-space: nowrap; }

/* confirm modal */
.ov { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center; padding: 1rem; }
.confirm { background: #fff; border-radius: 14px; padding: 1.4rem 1.5rem; max-width: 400px; width: 100%; box-shadow: 0 18px 50px rgba(0,0,0,0.3); }
.confirm h3 { color: var(--navy); margin-bottom: 0.5rem; }
.confirm p { color: var(--muted); margin: 0 0 1.2rem; }
.confirm-actions { display: flex; gap: 0.7rem; justify-content: flex-end; }

/* badges */
.badge { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 0.75rem; font-weight: 700; }
.badge.food { background: #fff0e6; color: #b5651d; }
.badge.modifier { background: #ede7ff; color: #6741d9; }
.badge.ingredient { background: #e6f4ea; color: #2f9e44; }
.badge.on { background: #e6f4ea; color: #2f9e44; }
.badge.off { background: #f1f3f5; color: #868e96; }
.badge.active { background: #e6f4ea; color: #2f9e44; }
.badge.inactive { background: #f1f3f5; color: #868e96; }
.badge.yes { background: #e7f0fb; color: #1f6fd6; }
.badge.no { background: #f1f3f5; color: #868e96; }
.badge.settled { background: #e6f4ea; color: #2f9e44; }
.badge.open { background: #e7f0fb; color: #1f6fd6; }
.badge.void { background: #ffe9e9; color: #e03131; }
.badge.asset { background: #e7f0fb; color: #1f6fd6; }
.badge.liability { background: #fff0e6; color: #b5651d; }
.badge.equity { background: #ede7ff; color: #6741d9; }
.badge.revenue { background: #e6f4ea; color: #2f9e44; }
.badge.expense { background: #ffe9e9; color: #e03131; }

/* ---- Buttons ---- */
.btn { display: inline-flex; align-items: center; gap: 0.4rem; border: 1px solid transparent; border-radius: 9px;
    padding: 0.55rem 1rem; font-size: 0.92rem; font-weight: 600; cursor: pointer; text-decoration: none; }
.btn-primary { background: linear-gradient(135deg, var(--copper), var(--copper2)); color: #fff; box-shadow: 0 4px 12px rgba(181,101,29,0.25); }
.btn-primary:hover { filter: brightness(1.05); }
.btn-outline { background: #fff; color: var(--navy); border-color: var(--line); }
.btn-outline:hover { border-color: var(--copper2); color: var(--copper); }
.btn-danger { background: #fff; color: #e03131; border-color: #f5c2c2; }
.btn-sm { padding: 0.35rem 0.7rem; font-size: 0.85rem; }
.btn:disabled { opacity: 0.55; cursor: default; }

/* ---- Forms ---- */
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.1rem 1.3rem; }
.form-grid .full { grid-column: 1 / -1; }
.field label { display: block; font-size: 0.85rem; font-weight: 600; color: #47525e; margin-bottom: 0.35rem; }
.field input[type=text], .field input[type=number], .field select, .field textarea {
    width: 100%; height: 2.7rem; border: 1px solid var(--line); border-radius: 9px; padding: 0 0.8rem; font-size: 0.95rem; outline: none; background: #fff; }
.field textarea { height: auto; padding: 0.6rem 0.8rem; min-height: 4.5rem; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--copper2); box-shadow: 0 0 0 3px rgba(217,131,36,0.14); }
.checks { display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem; }
.check { display: flex; align-items: center; gap: 0.5rem; font-size: 0.92rem; color: #47525e; }
.check input { width: 1.1rem; height: 1.1rem; accent-color: var(--copper2); }
.form-actions { display: flex; gap: 0.7rem; margin-top: 1.4rem; padding-top: 1.2rem; border-top: 1px solid var(--line); }

/* ---- States ---- */
.state { padding: 3rem 1rem; text-align: center; color: var(--muted); }
.state.err { color: #b3261e; }
.alert { background: #fdecec; border: 1px solid #f5c2c2; color: #b3261e; border-radius: 10px; padding: 0.7rem 0.9rem; margin-bottom: 1rem; font-size: 0.92rem; }
/* The bare .alert above is the FAILURE colour, and it is the wrong voice for an outcome that
   went fine. These two modifiers say what happened: .ok for work that was carried out, .warn
   for a request answered by doing the safer thing instead. */
.alert.ok   { background: #eaf7ef; border-color: #bfe3cd; color: #1c6b3d; }
.alert.warn { background: #fdf4e3; border-color: #f0dcae; color: #8a5a12; }
.spinner { width: 26px; height: 26px; border-radius: 50%; border: 3px solid rgba(31,75,116,0.2); border-top-color: var(--navy2);
    animation: spin .7s linear infinite; display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }
tr.total-row td { font-weight: 800; border-top: 2px solid var(--line); background: #fbfcfe; }

/* ---- Coming soon ---- */
.soon { text-align: center; padding: 3.5rem 1.5rem; }
.soon .soon-ic { font-size: 3rem; }
.soon h2 { font-size: 1.5rem; color: var(--navy); margin: 0.6rem 0 0.3rem; }
.soon p { color: var(--muted); max-width: 480px; margin: 0.3rem auto; }
.soon .pattern { display: inline-flex; gap: 0.6rem; margin-top: 1.4rem; flex-wrap: wrap; justify-content: center; }
.soon .tag { background: #eef2f6; color: var(--muted); border-radius: 20px; padding: 0.35rem 0.9rem; font-size: 0.85rem; font-weight: 600; }

/* ---- Reference-data entry fields ----
   Global rather than scoped to RefDialog: the dialog takes its body as a RenderFragment, so the
   markup belongs to the page that supplies it and a scoped stylesheet would never reach it. */
.rf-row { display: flex; flex-direction: column; gap: 6px; }
.rf-row > label { font-size: .82rem; font-weight: 700; color: #3d4756; }
.rf-in { border: 1px solid #d6dce6; border-radius: 10px; padding: 9px 12px; font-size: .94rem;
    background: #fff; color: #1f2733; width: 100%; }
.rf-in:focus { outline: 2px solid #c6ccf7; border-color: #4f5bd5; }
.rf-in.short { max-width: 170px; }
.rf-hint { color: #8a94a2; font-size: .78rem; line-height: 1.45; }

/* Reveal toggle on the credential fields (Employees).
   The button overlays the right of the input rather than sitting beside it, so the field keeps
   its full width and the row does not reflow when the icon swaps. Padding-right on the input
   reserves the space, otherwise a long password runs under the button. */
.rf-reveal { position: relative; display: block; }
.rf-reveal.short { max-width: 170px; }
.rf-reveal .rf-in { width: 100%; padding-right: 44px; }
.rf-eye {
    position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
    /* 36px painted, but the whole control is a comfortable target — the app's rule of
       paint small, hit large. */
    width: 36px; height: 36px;
    display: grid; place-items: center;
    border: 0; background: none; cursor: pointer;
    color: #8a94a2; border-radius: 8px;
}
.rf-eye:hover:not(:disabled) { color: #4f5bd5; background: #f1f3fe; }
.rf-eye:focus-visible { outline: 2px solid #c6ccf7; outline-offset: 1px; }
.rf-eye .bo-ico { width: 18px; height: 18px; }

/* Empty box: the toggle has nothing to reveal. Faded rather than hidden, so the control does not
   appear and disappear as you type — and it keeps its tooltip, which is where the explanation is. */
.rf-eye:disabled { opacity: .35; cursor: not-allowed; }
/* Clear-search on a picker field. The same overlay idea as .rf-reveal above and for the same
   reason — the input keeps its full width, and the row does not reflow when the cross appears
   and disappears with the text. Kept separate from .rf-reveal rather than folded into it: that
   one reserves 44px permanently for a control that is always present, whereas this one is only
   there while there is a query, and sharing the rule would leave a dead 44px gutter on every
   search box in the product. */
.rf-clearable { position: relative; display: block; }
.rf-clearable .rf-in { width: 100%; padding-right: 40px; }
.rf-clear {
    position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
    /* Paint small, hit large — the same rule the reveal toggle follows. */
    width: 32px; height: 32px;
    display: grid; place-items: center;
    border: 0; background: none; cursor: pointer;
    color: #8a94a2; border-radius: 8px;
    font-size: .8rem; line-height: 1;
}
.rf-clear:hover { color: #4f5bd5; background: #f1f3fe; }
.rf-clear:focus-visible { outline: 2px solid #c6ccf7; outline-offset: 1px; }

.rf-pair { display: flex; gap: 10px; flex-wrap: wrap; }
.rf-sw { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: .9rem; color: #3d4756; }
.rf-sw input { position: absolute; opacity: 0; width: 0; height: 0; }
.rf-swt { width: 42px; height: 24px; border-radius: 999px; background: #cfd6e0; position: relative; transition: background .15s; }
.rf-swt::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
    border-radius: 50%; background: #fff; transition: transform .15s; }
.rf-sw input:checked + .rf-swt { background: #2e9e63; }
.rf-sw input:checked + .rf-swt::after { transform: translateX(18px); }
.rf-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 12px 6px 18px; }
.rf-head h1 { font-size: 1.7rem; margin: 0 0 .3rem; color: #1f2733; }
.rf-lead { color: #5c6674; font-size: 1.02rem; margin: 0; max-width: 70ch; }
.rf-page { max-width: 1100px; margin: 0 auto; padding: 8px 4px; }

/* ---- Tax groups: ordered members ---- */
.tg-members { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; counter-reset: tgm; }
.tg-members li { background: #f6f8fc; border: 1px solid #e6ecf3; border-radius: 9px; padding: 9px 11px;
    display: flex; flex-direction: column; gap: 8px; }
.tg-m-hd { display: flex; align-items: center; gap: 10px; }
.tg-m-seq { width: 22px; height: 22px; border-radius: 50%; background: #4f5bd5; color: #fff;
    display: grid; place-items: center; font-size: .72rem; font-weight: 800; flex: none; }
.tg-m-nm { flex: 1; font-size: .9rem; font-weight: 650; color: #24313f; }
.tg-m-cd { font-size: .72rem; color: #8a94a2; font-weight: 700; margin-left: 6px; }
.tg-m-rate { font-size: .88rem; font-weight: 700; color: #1f2733; font-variant-numeric: tabular-nums; }
.tg-m-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding-top: 8px; border-top: 1px dashed #dbe3ee; }
.tg-m-acts .rf-sw { font-size: .82rem; margin-right: auto; }
.tg-total { font-size: .88rem; color: #3d4756; padding: 8px 2px; }

.sts-warn { margin: 14px 6px 0; background: #fdf3e3; border: 1px solid #e8d3a8; color: #8a5a12;
    padding: 10px 14px; border-radius: 10px; font-size: .88rem; }

/* ---- Scheduled pricing: the bulk pricing grid ---- */
.sp2 { max-width: 1280px; margin: 0 auto; padding: 8px 4px; color: #1f2733; }

/* Filter and action bars. Wrap rather than scroll — these are all equally important and hiding
   one behind an overflow is how a filter gets left set without anyone noticing. */
.sp2-bar, .sp2-apply { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap;
    background: #fff; border: 1px solid #e4e9f0; border-radius: 12px; padding: 14px 16px; margin: 0 6px 12px; }
.sp2-apply { background: #f7f9fc; }
.sp2-f { display: flex; flex-direction: column; gap: 5px; min-width: 150px; }
.sp2-f.grow { flex: 1; min-width: 220px; }
.sp2-f.narrow { min-width: 110px; }
.sp2-f > label { font-size: .74rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .4px; color: #8a94a2; }
.sp2-acts { display: flex; gap: 8px; margin-left: auto; }
.sp2-empty p { margin: 0 0 14px; }
/* The zone shown as a fact, not a control — it comes from the schedule and cannot be overridden. */
.sp2-fixed { display: flex; align-items: baseline; gap: 8px; padding: 9px 12px;
    border: 1px dashed #d6dce6; border-radius: 10px; background: #f7f9fc;
    font-size: .92rem; font-weight: 700; color: #1f2733; }
.sp2-fixed-note { font-size: .72rem; font-weight: 600; color: #8a94a2; text-transform: uppercase; letter-spacing: .3px; }
.sp2-basis { display: block; font-size: .66rem; font-weight: 600; color: #8a94a2;
    text-transform: none; letter-spacing: 0; margin-top: 1px; }

.sp2-empty { margin: 0 6px; padding: 48px 20px; text-align: center; color: #8a94a2;
    font-style: italic; background: #fff; border: 1px solid #e4e9f0; border-radius: 12px; }

/* Unsaved work is stated, counted, and reversible. One click here can change fifty prices. */
.sp2-dirty { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin: 0 6px 12px; padding: 11px 16px; border-radius: 10px;
    background: #fdf3e3; border: 1px solid #e8d3a8; color: #8a5a12; font-size: .88rem; }

.sp2-grid th.chk, .sp2-grid td.chk { width: 34px; text-align: center; }
.sp2-grid tr.sp2-changed { background: #fff8e8; }
.sp2-grid tr.sp2-changed td { border-bottom-color: #e8d3a8; }
.sp2-price { width: 92px; border: 1px solid #d7dfe9; border-radius: 7px; padding: 5px 8px;
    font-size: .88rem; text-align: right; font-variant-numeric: tabular-nums;
    color: #1f2733; background: #fff; }
.sp2-price:focus { outline: 2px solid #c6ccf7; border-color: #4f5bd5; }

/* Buttons shared by the reference screens. */
.rf-btn { border: 1px solid #d6dce6; background: #fff; color: #1f2733; border-radius: 10px;
    padding: 9px 16px; font-weight: 700; font-size: .88rem; cursor: pointer; white-space: nowrap; }
.rf-btn:hover:not(:disabled) { background: #f1f4f9; }
.rf-btn:disabled { opacity: .5; cursor: default; }
.rf-btn.primary { background: #4f5bd5; border-color: #4f5bd5; color: #fff; }
.rf-btn.primary:hover:not(:disabled) { background: #4450c4; }
.rf-btn.ghost { background: transparent; }
.rf-btn.sm { padding: 6px 12px; font-size: .82rem; }

/* THE ICON SIZE, and it is load-bearing rather than decorative — see the note above .rp-ok.

   BoIcon renders <svg viewBox="0 0 24 24"> with no width or height. That is a replaced element with
   no intrinsic size, so a browser that is told nothing falls back to 300x150. This rule was missing,
   which is why the printer on Transaction History's "Print list" came out as a 300px block: the same
   button in the order sheet looked right only because .ordv-h-a happens to size icons for its own
   row, and the toolbar had nothing equivalent.

   Sized on .rf-btn itself rather than on that one toolbar, because the fault was never local to it —
   every .rf-btn anywhere in the Back Office carries an unsized icon the moment somebody puts one in,
   and the next occurrence would be the same 300px surprise in a different screen. 15px matches the
   Print button in the order sheet, so the two now agree. */
.rf-btn .bo-ico { width: 15px; height: 15px; vertical-align: -2px; }

/* ---- Scheduled pricing: weekday picker ---- */
.sp-days { display: flex; gap: 6px; flex-wrap: wrap; }
.sp-day { border: 1px solid #d6dce6; border-radius: 8px; padding: 5px 10px; font-size: .8rem;
    font-weight: 700; color: #55636f; cursor: pointer; user-select: none; background: #fff; }
.sp-day.on { background: #4f5bd5; border-color: #4f5bd5; color: #fff; }
.sp-day input { display: none; }

/* ---- Purchase order lines ---- */
.po-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.po-lines li { background: #f6f8fc; border: 1px solid #e6ecf3; border-radius: 9px; padding: 9px 11px;
    display: flex; flex-direction: column; gap: 8px; }
.po-l-hd { display: flex; align-items: center; gap: 10px; }
.po-l-seq { width: 22px; height: 22px; border-radius: 50%; background: #4f5bd5; color: #fff;
    display: grid; place-items: center; font-size: .72rem; font-weight: 800; flex: none; }
.po-l-nm { flex: 1; font-size: .9rem; font-weight: 650; color: #24313f; }
.po-l-cd { font-size: .72rem; color: #8a94a2; font-weight: 700; margin-left: 6px; }
.po-l-fields { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
    padding-top: 8px; border-top: 1px dashed #dbe3ee; }
.po-l-f { display: flex; flex-direction: column; gap: 4px; font-size: .74rem; color: #6b7684; font-weight: 600; }
.po-l-f input, .po-l-f select { width: 96px; border: 1px solid #d7dfe9; border-radius: 7px;
    padding: 5px 8px; font-size: .85rem; color: #24313f; background: #fff; }
.po-l-tot { margin-left: auto; font-weight: 700; font-size: .9rem; color: #1f2733; }
.po-l-rec { font-size: .72rem; color: #2f9e44; font-weight: 700; }
.po-total { padding: 10px 2px; font-size: .92rem; color: #3d4756; }

/* ---- Receipt settings ---- */
.rc-card { background: #fff; border: 1px solid #e4e9f0; border-radius: 16px; padding: 22px;
    display: flex; flex-direction: column; gap: 14px; max-width: 640px;
    box-shadow: 0 1px 3px rgba(20,30,50,.05), 0 12px 30px rgba(20,30,50,.04); }
.rc-card h4 { margin: 8px 0 0; font-size: 1rem; color: #1f2733; }
.rc-card h4:first-child { margin-top: 0; }
.rc-meta { font-size: .76rem; color: #8a94a2; border-top: 1px solid #eef1f6; padding-top: 12px; }

/* ---- Reports ---- */
/* The icon rule is not decoration, it is load-bearing. An <svg> with a viewBox and no width or
   height is a replaced element with no intrinsic size, so a browser falls back to 300x150 — the
   save tick on /date-format rendered at that size and pushed the whole page apart. There is no
   base .bo-ico rule in this file by design (every container picks its own size), which means a
   container that forgets one does not get a small icon, it gets a 300px one. Sized here to match
   .rp-err below, which has had the pair since it was written. */
.rp-ok { margin-top: 14px; background: #eaf7f0; color: #1c6b45; padding: 10px 14px; border-radius: 10px; font-size: .88rem;
    display: flex; align-items: center; gap: 8px; }
.rp-ok .bo-ico { width: 17px; height: 17px; flex: none; }
.rp-warn { margin-top: 14px; background: #fdf3e3; color: #8a5a12; padding: 10px 14px; border-radius: 10px; font-size: .88rem; }

/* =============================================================================================
   REPORTING MODULE.

   On the design-system tokens (--c-*), not the legacy palette above. Everything from here down
   belongs to /reports: the browser shell, the filter bar, the read-only grid, the stat tiles and
   the SVG charts.

   THE CHART INK IS ITS OWN SET OF TOKENS (--rc-*) rather than raw hex in the components, so the
   light and dark values live in one place and an SVG attribute can reference them. The mark hue
   is the product's own --c-nav blue on light and the matching dark step, both checked at >= 3:1
   against the card surface they are drawn on; the brand saffron is deliberately NOT used for a
   mark, because it measures 2.04:1 on white and a bar nobody can see is not a bar.
   ============================================================================================= */

:root {
    --rc-mark: #1F6FB2;
    --rc-wash: rgba(31, 111, 178, .12);
    --rc-grid: var(--c-line);
    --rc-axis: var(--c-line-strong);
}

:root[data-theme="dark"] {
    --rc-mark: #3987e5;
    --rc-wash: rgba(57, 135, 229, .18);
}

/* ---- the browser: rail + report ---- */

/* The page: the shared filter bar, then the rail and the report side by side beneath it. The bar
   is a sibling of .rp2 rather than a child of .rp2-main, because it governs every report on the
   rail and not the one currently open. */
.rp2-page { max-width: 1500px; margin: 0 auto; }

.rp2 { display: flex; gap: 22px; align-items: flex-start; }

/* Shown in the bar's place for the two ledger reports, which have no period. Explaining the
   absence costs one line and stops the bar's disappearance reading as a fault. */
.rp2-nofilter {
    margin: 0 0 18px; padding: 11px 15px;
    background: var(--c-s2); border: 1px solid var(--c-line);
    border-left: 3px solid var(--c-line-strong); border-radius: var(--r-md);
    font-size: .85rem; color: var(--c-text-dim);
}

.rp2-rail {
    width: 208px; flex: 0 0 208px;
    position: sticky; top: calc(var(--bo-top-h) + 17px);
    background: var(--c-s2); border: 1px solid var(--c-line);
    border-radius: var(--r-md); padding: 10px 8px 14px;
}
.rp2-rail-h {
    font-size: .7rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint); padding: 4px 8px 8px;
}
.rp2-grp {
    font-size: .68rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint);
    padding: 12px 8px 4px; border-top: 1px solid var(--c-line); margin-top: 6px;
}
.rp2-grp:first-of-type { border-top: 0; margin-top: 0; }
.rp2-link {
    display: block; padding: 7px 9px; margin: 1px 0; border-radius: var(--r-sm);
    font-size: .875rem; color: var(--c-text-dim); text-decoration: none;
    border-left: 3px solid transparent;
}
.rp2-link:hover { background: var(--c-s4); color: var(--c-text); }
.rp2-link.on {
    background: var(--c-accent-wash); color: var(--c-accent-text);
    border-left-color: var(--c-accent); font-weight: 700;
}

.rp2-main { flex: 1; min-width: 0; }

/* The filter, restated in words under the bar. Numbers without their filter get acted on. */
.rp2-scope {
    margin: 10px 2px 16px; font-size: .82rem; color: var(--c-text-dim);
    font-weight: 600;
}

.rp2-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 12px; margin-bottom: 8px; }
.rp2-card {
    display: flex; flex-direction: column; gap: 5px;
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md);
    padding: 14px 16px; text-decoration: none;
}
.rp2-card:hover { border-color: var(--c-accent); background: var(--c-accent-wash); }
.rp2-card-t { font-weight: 700; font-size: .96rem; color: var(--c-text); }
.rp2-card-d { font-size: .82rem; color: var(--c-text-dim); line-height: 1.45; }
.rp2-card-go { font-size: .78rem; font-weight: 700; color: var(--c-accent-text); margin-top: 2px; }

/* ---- the filter bar ---- */

/* Full width, above the rail. The accent hairline on the left is the one bit of chrome saying
   "this governs everything below", which is the whole reason it is not inside the report panel. */
.rfb {
    background: var(--c-s2); border: 1px solid var(--c-line);
    border-left: 3px solid var(--c-accent); border-radius: var(--r-md);
    padding: 12px 15px; margin-bottom: 18px;
    display: flex; flex-direction: column; gap: 11px;
}

.rfb-cap { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.rfb-cap-t {
    font-size: .68rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text);
}
.rfb-cap-s { font-size: .76rem; color: var(--c-text-faint); }

.rfb-presets { display: flex; flex-wrap: wrap; gap: 6px; }
.rfb-p {
    border: 1px solid var(--c-line-strong); background: var(--c-s3); color: var(--c-text-dim);
    border-radius: var(--r-pill); padding: 5px 12px; font-size: .82rem; font-weight: 650;
    cursor: pointer; white-space: nowrap;
}
.rfb-p:hover { border-color: var(--c-accent); color: var(--c-text); }
.rfb-p.on {
    background: var(--c-accent); border-color: var(--c-accent);
    color: var(--c-accent-ink); font-weight: 750;
}

.rfb-custom { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-size: .84rem; color: var(--c-text-dim); }
.rfb-custom input {
    margin-left: 6px; border: 1px solid var(--c-line-strong); border-radius: var(--r-sm);
    padding: 5px 8px; font-size: .84rem; background: var(--c-s3); color: var(--c-text);
}

.rfb-scope { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; padding-top: 10px; border-top: 1px dashed var(--c-line); }
.rfb-f { display: flex; flex-direction: column; gap: 4px; min-width: 150px; }
.rfb-f > label {
    font-size: .68rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint);
}
.rfb-f select, .rfb-f input {
    border: 1px solid var(--c-line-strong); border-radius: var(--r-sm);
    padding: 6px 9px; font-size: .86rem; background: var(--c-s3); color: var(--c-text);
}
.rfb-f select:focus, .rfb-f input:focus, .rfb-custom input:focus { outline: 2px solid var(--c-accent); outline-offset: -1px; }
.rfb-time { min-width: 210px; }
.rfb-pair { display: flex; align-items: center; gap: 5px; }
.rfb-pair input { width: 92px; }
.rfb-hint {
    display: inline-grid; place-items: center; width: 14px; height: 14px; margin-left: 3px;
    border-radius: 50%; background: var(--c-s4); color: var(--c-text-faint);
    font-size: .62rem; font-weight: 800; cursor: help;
}
.rfb-x { border: 0; background: none; color: var(--c-text-faint); cursor: pointer; font-size: .8rem; padding: 4px; }
.rfb-x:hover { color: var(--c-stop-text); }

.rfb-now { margin-left: auto; display: flex; flex-direction: column; gap: 3px; align-items: flex-end; text-align: right; }
.rfb-range { font-size: .86rem; font-weight: 750; color: var(--c-text); }
.rfb-bad { font-size: .78rem; font-weight: 650; color: var(--c-stop-text); display: inline-flex; align-items: center; gap: 5px; }
.rfb-bad .bo-ico { width: 14px; height: 14px; }

/* ---- report chrome ---- */

.rp-h { font-size: .95rem; font-weight: 750; color: var(--c-text); margin: 22px 2px 10px; }

/* Menu heading in the Item Sales by Menu Group breakdown. Larger and ruled off, because it is a
   level ABOVE the group headings under it and the reader has to be able to see where one menu
   ends. Extra top margin so a menu never looks like it belongs to the table above it. */
.rp-hm {
    font-size: 1.05rem; margin: 34px 2px 4px;
    padding-bottom: 8px; border-bottom: 2px solid var(--c-line);
}
.rp-hm:first-of-type { margin-top: 22px; }

/* Group heading: the name on the left, what it earned on the right. The figure is here rather
   than only in the table's total row so a reader scanning menus can compare groups without
   reading each table to its end. */
.rp-hg {
    display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
    margin: 18px 2px 6px;
}
.rp-hg-n { font-size: .9rem; font-weight: 700; color: var(--c-text); }
.rp-hg-f { font-size: .82rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.rp-card {
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md);
    padding: 14px 16px; margin: 16px 0;
}
.rp-two, .rp-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
.rp-grid2 > .rp-card { margin: 16px 0 0; }

.rp-err {
    display: flex; align-items: center; gap: 8px; margin: 12px 0;
    background: color-mix(in srgb, var(--c-stop) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--c-stop) 35%, transparent);
    color: var(--c-stop-text); border-radius: var(--r-sm); padding: 10px 13px; font-size: .88rem;
}
.rp-err .bo-ico { width: 17px; height: 17px; flex: none; }

.rp-load { display: flex; align-items: center; gap: 10px; padding: 34px 8px; color: var(--c-text-dim); font-size: .9rem; }
.rp-none { padding: 40px 20px; text-align: center; color: var(--c-text-faint); font-style: italic;
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md); margin: 14px 0; }
.rp-none.sm { padding: 16px; margin: 6px 0; }

.rp-note { font-size: .82rem; color: var(--c-text-dim); line-height: 1.55; margin: 12px 2px; max-width: 88ch; }
.rp-note.warn {
    display: flex; align-items: flex-start; gap: 8px;
    background: var(--c-accent-wash); border-radius: var(--r-sm);
    padding: 10px 13px; color: var(--c-warn); font-weight: 600;
}
.rp-note.warn .bo-ico { width: 16px; height: 16px; flex: none; margin-top: 1px; }

/* Per-report filters — inside a grid's toolbar, or on their own bar above one. */
.rp-fbar {
    display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap;
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md);
    padding: 12px 14px; margin-bottom: 14px;
}
.rp-f { display: flex; flex-direction: column; gap: 4px; min-width: 140px; }
.rp-f.narrow { min-width: 96px; }
.rp-f.grow { flex: 1; min-width: 170px; }
.rp-f > label {
    font-size: .66rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint);
}
.rp-f select, .rp-f input {
    border: 1px solid var(--c-line-strong); border-radius: var(--r-sm);
    padding: 6px 9px; font-size: .85rem; background: var(--c-s3); color: var(--c-text); width: 100%;
}
.rp-f select:focus, .rp-f input:focus { outline: 2px solid var(--c-accent); outline-offset: -1px; }

/* A toolbar cell holding an ACTION rather than a field.

   Both overrides undo something .rp-f does for fields and gets wrong for buttons. A field wants a
   140px floor so a row of dropdowns lines up and none collapses to the width of its own longest
   option; a button has no such relationship with its neighbours, and inheriting that floor stretched
   a two-word button into a block half again as wide as its text. The column also stretches its
   children to fill it, which is right for a select and is what made the button meet that floor in
   the first place.

   Bottom alignment is not lost with it: .rg-bar-l already aligns its cells to flex-end, so an action
   cell sits on the same baseline as the fields without needing an empty label to prop it there. */
.rp-f.rp-f-act { min-width: 0; }
.rp-f.rp-f-act > .rf-btn { align-self: flex-start; }

/* ---- stat tiles ---- */

/* Exactly one hero per view: net sales. The rest are tiles, so the eye is told where to start. */
.rk-hero {
    display: flex; align-items: stretch; gap: 20px; flex-wrap: wrap;
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md);
    padding: 18px 20px; margin-bottom: 14px;
}
.rk-hero-l { flex: 1; min-width: 240px; display: flex; flex-direction: column; gap: 3px; }
.rk-hero-v {
    font-size: 2.9rem; font-weight: 800; color: var(--c-text);
    letter-spacing: var(--track-display); line-height: 1.05;
}
.rk-hero-r {
    display: flex; flex-direction: column; justify-content: center; gap: 5px; min-width: 190px;
    padding-left: 20px; border-left: 1px solid var(--c-line);
}
.rk-line { display: flex; justify-content: space-between; gap: 20px; font-size: .88rem; color: var(--c-text-dim); }
.rk-line b { color: var(--c-text); font-variant-numeric: tabular-nums; }
.rk-line.tot { border-top: 1px solid var(--c-line); padding-top: 5px; margin-top: 2px; font-weight: 700; }

/* NOT A RUNG OF THE SUM. Indented and quieter than the lines around it, so the eye can see at a
   glance which figures are being added and which are there for information — the tax already
   inside the price, and tips, neither of which is part of Taken. */
.rk-line.sub { padding-left: 14px; font-size: .82rem; opacity: .82; }

.rk-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); gap: 12px; margin-bottom: 14px; }
.rk {
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md);
    padding: 13px 15px; display: flex; flex-direction: column; gap: 2px;
}
.rk.warn { border-left: 3px solid var(--c-accent); }
.rk-lbl {
    font-size: .68rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint);
}
.rk-v { font-size: 1.5rem; font-weight: 800; color: var(--c-text); letter-spacing: var(--track-tight); }
.rk-foot { font-size: .76rem; color: var(--c-text-dim); line-height: 1.4; }

/* ---- read-only report grid ---- */

.rg { margin-bottom: 8px; }
.rg-bar { display: flex; align-items: flex-end; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; }
.rg-bar-l { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.rg-search { position: relative; min-width: 190px; }
.rg-search input {
    width: 100%; border: 1px solid var(--c-line-strong); border-radius: var(--r-sm);
    padding: 7px 28px 7px 10px; font-size: .85rem; background: var(--c-s3); color: var(--c-text);
}
.rg-search input:focus { outline: 2px solid var(--c-accent); outline-offset: -1px; }
.rg-clear {
    position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
    border: 0; background: none; color: var(--c-text-faint); cursor: pointer; padding: 4px 6px; font-size: .75rem;
}
.rg-clear:hover { color: var(--c-text); }

.rg-wrap {
    background: var(--c-s2); border: 1px solid var(--c-line); border-radius: var(--r-md);
    overflow: auto; max-height: 620px;
}
table.rg-tbl { width: 100%; border-collapse: collapse; font-size: .855rem; }
table.rg-tbl thead th {
    position: sticky; top: 0; z-index: 1;
    text-align: left; background: var(--c-s1); color: var(--c-text-faint);
    font-weight: 800; font-size: .68rem; text-transform: uppercase; letter-spacing: var(--track-label);
    padding: 9px 11px; border-bottom: 1px solid var(--c-line); white-space: nowrap;
}
table.rg-tbl thead th.srt { cursor: pointer; user-select: none; }
table.rg-tbl thead th.srt:hover { color: var(--c-accent-text); }
table.rg-tbl thead th .arw { color: var(--c-accent-text); font-size: .66rem; }
table.rg-tbl tbody td { padding: 8px 11px; border-bottom: 1px solid var(--c-line); color: var(--c-text); vertical-align: top; }
table.rg-tbl tbody tr:hover { background: var(--c-s4); }
table.rg-tbl tbody tr.hit { cursor: pointer; }
table.rg-tbl td.num, table.rg-tbl th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.rg-tbl tfoot td {
    position: sticky; bottom: 0;
    font-weight: 800; background: var(--c-s1); border-top: 2px solid var(--c-line-strong);
    padding: 9px 11px; color: var(--c-text);
}
table.rg-tbl tfoot td.num { text-align: right; font-variant-numeric: tabular-nums; }

.rg-empty { padding: 40px 16px; text-align: center; color: var(--c-text-faint); font-style: italic; }
.rg-empty button { margin-left: 10px; }

/* Share-of-total: the number carries it, the bar only makes the ranking scannable. */
.rg-share, .rg-share-h { width: 108px; }
.rg-share-t { display: block; font-size: .74rem; color: var(--c-text-dim); font-variant-numeric: tabular-nums; text-align: right; }
.rg-share-b { display: block; height: 4px; border-radius: 2px; background: var(--c-s4); margin-top: 3px; overflow: hidden; }
.rg-share-b > span { display: block; height: 100%; background: var(--rc-mark); border-radius: 2px; }

/* ---- SVG charts ---- */

.ch { margin: 0; }
.ch-cap { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; }
.ch-ttl { font-size: .92rem; font-weight: 750; color: var(--c-text); }
.ch-sub { font-size: .78rem; color: var(--c-text-dim); }
.ch-svg { display: block; width: 100%; height: auto; overflow: visible; }
.ch-empty { padding: 34px 12px; text-align: center; color: var(--c-text-faint); font-style: italic; font-size: .86rem; }

.ch-grid { stroke: var(--rc-grid); stroke-width: 1; }
.ch-axis { stroke: var(--rc-axis); stroke-width: 1; }

/* Axis and value text wears text tokens, never the mark colour. */
.ch-tick, .ch-cat { fill: var(--c-text-faint); font-size: 11px; }
.ch-val { fill: var(--c-text); font-size: 11.5px; font-weight: 750; }

.ch-mark { fill: var(--rc-mark); }
.ch-line { fill: none; stroke: var(--rc-mark); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.ch-area { fill: var(--rc-wash); stroke: none; }

/* The hover layer. A transparent band per category is the hit target — far bigger than the mark,
   so a quiet hour is still hoverable — and it carries the <title> the browser shows. */
.ch-hit { fill: transparent; }
.ch-col:hover .ch-mark, .ch-bar:hover .ch-mark { fill: var(--c-accent-text); }

.ch-cross { stroke: var(--rc-axis); stroke-width: 1; opacity: 0; }
.ch-pt:hover .ch-cross { opacity: 1; }

/* Dots carry a 2px ring in the surface colour so they stay legible over the line. */
.ch-dot { fill: var(--rc-mark); stroke: var(--c-s2); stroke-width: 2; opacity: 0; }
.ch-dot.on { opacity: 1; }
.ch-pt:hover .ch-dot { opacity: 1; }

/* ---- order / item drill-down dialog ---- */

.rp-dlg {
    background: var(--c-s2); border-radius: var(--r-lg); width: min(1080px, 96vw);
    max-height: 90vh; display: flex; flex-direction: column;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .34); border: 1px solid var(--c-line);
}
.rp-dlg-h {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
    padding: 16px 20px; border-bottom: 1px solid var(--c-line);
}
.rp-dlg-h h3 { font-size: 1.15rem; color: var(--c-text); }
.rp-dlg-sub { margin: 3px 0 0; font-size: .82rem; color: var(--c-text-dim); }
.rp-dlg-b { padding: 16px 20px 20px; overflow-y: auto; }
.rp-dlg-b h4 { margin: 18px 0 7px; font-size: .82rem; text-transform: uppercase;
    letter-spacing: var(--track-label); color: var(--c-text-faint); font-weight: 800; }
.rp-dlg-b h4:first-child { margin-top: 0; }
.rd-x { border: 0; background: none; cursor: pointer; color: var(--c-text-faint); padding: 4px; }
.rd-x:hover { color: var(--c-text); }

.rp-facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px 18px; margin: 0 0 4px; }
.rp-facts div { display: flex; flex-direction: column; gap: 1px; }
.rp-facts dt { font-size: .66rem; font-weight: 800; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--c-text-faint); }
.rp-facts dd { margin: 0; font-size: .88rem; color: var(--c-text); }

table.rp-mini { width: 100%; border-collapse: collapse; font-size: .83rem; }
table.rp-mini th {
    text-align: left; font-size: .66rem; text-transform: uppercase; letter-spacing: var(--track-label);
    color: var(--c-text-faint); font-weight: 800; padding: 6px 9px; border-bottom: 1px solid var(--c-line);
}
table.rp-mini td { padding: 7px 9px; border-bottom: 1px solid var(--c-line); color: var(--c-text); vertical-align: top; }
table.rp-mini .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.rp-mini tr.vd td { color: var(--c-text-faint); text-decoration: line-through; }
table.rp-mini tr.vd td .badge { text-decoration: none; }

.rp-code { font-size: .72rem; color: var(--c-text-faint); margin-left: 6px; }
.rp-mods { font-size: .76rem; color: var(--c-text-dim); margin-top: 2px; }
.rp-mods.note { font-style: italic; }

.rp-dlg-tot {
    margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--c-line);
    display: flex; flex-direction: column; gap: 4px; margin-left: auto; max-width: 300px;
}
.rp-dlg-tot div { display: flex; justify-content: space-between; gap: 24px; font-size: .88rem; color: var(--c-text-dim); }
.rp-dlg-tot b { color: var(--c-text); font-variant-numeric: tabular-nums; }
.rp-dlg-tot .tot { border-top: 1px solid var(--c-line); padding-top: 5px; margin-top: 3px; font-size: 1rem; font-weight: 800; }

/* Statuses this module shows that the legacy badge list did not cover. */
.badge.held { background: #fff4e0; color: #8a5a12; }
.badge.scheduled { background: #ede7ff; color: #6741d9; }
.badge.refund { background: #ffe9e9; color: #e03131; }

@media (max-width: 1180px) {
    .rp-two, .rp-grid2 { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
    .rp2 { flex-direction: column; }
    .rp2-rail { width: 100%; flex: none; position: static; }
    .rk-hero-r { border-left: 0; padding-left: 0; border-top: 1px solid var(--c-line); padding-top: 12px; }
}

.rl-module { border: 1px solid #e6ecf3; border-radius: 10px; overflow: hidden; }
.rl-module-hd { background: #f6f8fc; padding: 7px 11px; font-size: .72rem; font-weight: 800;
    text-transform: uppercase; letter-spacing: .5px; color: #55636f; border-bottom: 1px solid #e6ecf3; }
.rl-perm { display: flex; align-items: center; gap: 9px; padding: 7px 11px; cursor: pointer; }
.rl-perm:hover { background: #f5f8ff; }
.rl-perm-nm { flex: 1; font-size: .88rem; color: #24313f; }
.rl-perm-cd { font-size: .72rem; color: #8a94a2; font-weight: 700; }

/* validation (framework) */
.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }
.invalid { outline: 1px solid #e50000; }
.validation-message { color: #e50000; }

#blazor-error-ui { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: #b32121; color: #fff; padding: 0.7rem 1rem; z-index: 1000; }
#blazor-error-ui .reload, #blazor-error-ui .dismiss { color: #fff; margin-left: 0.6rem; cursor: pointer; }

/* =============================================================================================
   RESPONSIVE

   The breakpoints, once, so the rest of this file and the scoped page stylesheets agree:

     1100px   laptop      two-column report layouts collapse to one
      860px   tablet      the sidebar becomes a drawer; this is where "mobile" starts
      560px   phone       toolbars stack, tables become cards, padding tightens

   WHAT WAS HERE BEFORE, and why it was not enough: `.bo { flex-direction: column }` with a
   full-width static sidebar. Technically responsive, unusable in practice — the whole nav menu
   sat above every page, so reaching any content meant scrolling past the entire menu, on every
   navigation. A back office has too many nav entries for that to be a stack.
   ============================================================================================= */

/* --- things that must never overflow, at any width ---------------------------------------- */

/* min-width:0 is the fix for the classic flex overflow: a flex child's default min-width is
   AUTO, meaning "never shrink below my content", so one wide table or one long unbroken string
   pushes the whole page sideways and the sticky bar detaches from the viewport. Nothing about
   this is mobile-specific, which is why it sits outside the media queries. */
.bo-main, .bo-content { min-width: 0; }

img, svg, video, canvas { max-width: 100%; height: auto; }

/* Codes, ids and emails have no spaces to break at, and a single long one is enough to widen
   the page. TABLE CELLS ONLY — this was briefly on .bo-store/.bo-user too, and "anywhere" means
   exactly that: the store pill broke "SAYA" into SA / YA and the button broke "Sign out" across
   three lines. Breaking mid-word is right for a 30-character product code in a column and wrong
   for every short label in a toolbar. The bar is handled by truncation instead, below. */
td, th { overflow-wrap: anywhere; }

@media (max-width: 860px) {

    /* --- the drawer ----------------------------------------------------------------------
       Off-canvas rather than stacked. Needs the burger button and scrim in MainLayout; without
       them the sidebar is simply hidden, which is a worse failure than the stack — so the
       markup and this rule ship together. */

    .bo-side {
        position: fixed;
        top: 0; left: 0; bottom: 0;
        z-index: 60;
        width: min(86vw, 300px);
        flex: none;
        /* dvh, not vh: on mobile browsers vh is the height with the URL bar HIDDEN, so a
           100vh drawer runs under the address bar and its last nav item cannot be reached. */
        height: 100dvh;
        transform: translateX(-100%);
        transition: transform .22s ease;
        border-right: 1px solid var(--glass-line);
        box-shadow: 0 0 40px rgba(0,0,0,.28);
        /* The pane is translucent by design on desktop, where a mesh sits behind it. Over page
           content it has to be opaque or the text behind shows through the menu. */
        background: var(--c-surface, #fff);
    }

    .bo-nav-cb:checked ~ .bo-side { transform: none; }

    /* The bar owns the drawer's z-context on desktop (z-index:10) but must sit UNDER the scrim
       and drawer once they exist, or the menu slides out behind the header. */
    .bo-top { z-index: 40; }

    .bo-scrim {
        display: block;
        position: fixed; inset: 0; z-index: 50;
        background: rgba(12,18,28,.44);
        opacity: 0; pointer-events: none;
        transition: opacity .22s ease;
    }

    .bo-nav-cb:checked ~ .bo-scrim { opacity: 1; pointer-events: auto; }

    .bo-burger { display: inline-flex; }

    /* --- the frame ------------------------------------------------------------------------ */

    .bo-content { padding: 12px 12px 28px; }

    .bo-top {
        gap: 8px;
        padding-left: 8px; padding-right: 10px;
    }

    /* The store picker keeps its place; the operator's NAME goes. Which store you are editing is
       a fact you can act on wrongly, so it survives at every width — whose account it is, on a
       personal phone, is not. The avatar and Sign out stay. */
    .bo-user > span:not(.bo-avatar) { display: none; }

    /* Nothing in the bar wraps. Every item here is a short label or a code, and a bar that grows
       to two or three lines pushes the page down and takes the sticky header's height with it —
       --bo-top-h is a fixed number that other sticky elements offset against, so a bar that is
       secretly 90px tall puts every page toolbar in the wrong place. */
    .bo-top > *, .bo-store, .bo-user, .bo-storepick, .bo-signout { white-space: nowrap; }

    /* The tenant name is the one genuinely variable-length thing up here, so it is the one that
       gives: it truncates rather than squeezing the controls beside it off the screen. */
    .bo-store { min-width: 0; flex: 1 1 auto; overflow: hidden; }
    .bo-store > span:first-child { overflow: hidden; text-overflow: ellipsis; }

    .bo-storepick, .bo-user, .bo-burger { flex: 0 0 auto; }

    .form-grid { grid-template-columns: 1fr; }

    /* Reverse of the "MOUSE, NOT TOUCH" note in the shell section above, and deliberately so:
       that reasoning holds for a browser on a desk, and stops holding the moment the same
       browser is a phone. 38px nav rows are a miss-tap on a thumb. */
    .bo-side a, .bo-side button { min-height: 44px; }

    /* iOS zooms the whole page in when a focused input is under 16px, and never zooms back out.
       Every "the layout breaks when I tap a field" report starts here.

       WHY !important, which is the only use of it in this file.

       Blazor scoped css compiles a page rule such as ".gc-card-body .rf-in" into
       ".gc-card-body .rf-in[b-7pp2vc5jcw]" — the scope attribute adds a full class-level of
       specificity. So any page's own stylesheet outranks a global rule here regardless of how the
       global one is written or where it sits in the file. This was measured, not assumed: at
       (0,1,0) it lost to that page rule at (0,3,0) and the fields stayed at 0.88rem. Matching it
       per page would mean editing every .razor.css that ever styles a field, and missing one
       silently reintroduces the fault on that page alone.

       16px is a platform threshold, not a typographic preference, so overriding page styling is
       the intended behaviour. Confined to the mobile media query; desktop typography is
       untouched. */
    input, select, textarea { font-size: 16px !important; }
}

@media (max-width: 560px) {
    .bo-content { padding: 10px 10px 24px; }

    /* The TENANT name goes; the STORE pill stays. At this width the name truncated to "S.." —
       which identifies nothing while still taking the room the pill needs. The pill is the
       actionable half of that pair anyway: it says which store you are about to change, and it
       is the control that changes it. */
    .bo-store > span:first-child { display: none; }

    /* Filter and toolbar rows: designed as horizontal strips with min-widths, which at 360px
       either overflow or squeeze every field to unusable. Full-width stacking is the only
       honest answer for a form on a phone. */
    .sp2-f, .sp2-f.grow, .sp2-f.narrow,
    .rfb-f, .rfb-time, .rp-f, .rp-f.grow, .rg-search {
        min-width: 0;
        width: 100%;
    }

    .rf-in.short, .rf-reveal.short { max-width: none; }

    /* Actions that sat to the right of a heading move under it, full width, so they are not two
       half-height buttons crushed against the edge. */
    .bo-actions, .rg-actions { flex-direction: column; align-items: stretch; }
    .bo-actions > *, .rg-actions > * { width: 100%; }

    /* A report's title row: heading on the left, "Printable version" on the right, as a nowrap
       flex row. At 295px it held 413px of content, so the report page scrolled sideways and the
       title was squeezed into a 128px column that broke "Sales Summary" across two lines while
       the button kept its full width beside it. The heading gets the width; the action goes
       under it. */
    .rf-head { flex-direction: column; align-items: stretch; gap: 10px; }
    .rf-head > * { min-width: 0; }

    /* The report body itself, so a wide child scrolls its own box rather than the page. */
    .rf-page, .rp2-page, .rp-page { min-width: 0; max-width: 100%; }

    /* The document toolbar above the printed sheet: title on the left, actions on the right, both
       flex:0 0 auto and neither allowed to wrap. Its actions block measured 404px, so the toolbar
       ran to 540px inside a 440px viewport and took the whole PAGE sideways with it — which also
       made the fixed nav scrim 539px wide, since a fixed element sizes to a document that has
       grown. Stacking the toolbar fixes both. */
    .rp-toolbar { flex-direction: column; align-items: stretch; gap: 8px; }
    .rp-toolbar-a { flex-wrap: wrap; }
    .rp-toolbar-a > * { flex: 1 1 auto; justify-content: center; }

    /* Sticky under the app bar is right on a desktop. Here the toolbar is now two rows tall, and
       pinning it costs a third of the screen on the one page whose entire purpose is looking at a
       document. It scrolls away with the content instead. */
    .rp-toolbar { position: static; }
}

/* =============================================================================================
   TABLES AS CARDS

   Below 560px each row becomes a labelled block: the field name on the left, its value on the
   right, one line per column. Nothing is hidden — these are financial screens, and a total read
   without the column that qualifies it is worse than a total not shown.

   HOW TO CONVERT A TABLE: add class="t-cards" to the <table>, and data-label="Column name" to
   every <td>. The label comes from the attribute rather than from the <th> because CSS cannot
   read another element's text — there is no selector for "the header above this cell".

   A table WITHOUT data-label attributes still degrades safely: it keeps its shape and scrolls
   inside .t-wrap rather than breaking the page. So .t-wrap is worth adding even to a table you
   have not labelled yet.
   ============================================================================================= */

.t-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }

/* Not inside a media query: the band between 560px and 860px is where the cards have not kicked
   in yet and the grid is still wider than the screen, and a laptop window dragged narrow hits it
   too. Scrolling the grid is always better than scrolling the page. */
.grid-wrap, .rg-wrap, .rf-tablewrap { overflow-x: auto; max-width: 100%; }

/* THE ONE TABLE THAT MUST NOT BECOME CARDS.
   .sheet-tbl is the printable document in ReportPaper — a facsimile of a paper report whose
   columns are composed to a fixed width by the projection that builds it. Its own comment puts it
   plainly: "the alignment IS the layout". Stacking those rows would not make the document
   readable on a phone, it would destroy the thing being reproduced. So it scrolls sideways in its
   own box, deliberately, and is the only table in the project without t-cards. */
.sheet-wrap, .sheet-tbl { max-width: 100%; }
.sheet-tbl { display: table; }

@media (max-width: 560px) {
    .t-cards { border: 0; width: 100%; }

    /* Hidden rather than removed: screen readers still announce the row's own cells, which carry
       their labels, so no information is lost with the header row. */
    .t-cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

    /* tfoot IS IN THIS LIST DELIBERATELY. Left out, it stays a table-footer-group while its rows
       are blocks, so the browser wraps them in an anonymous table box and the totals row
       shrink-wraps to its text — a 144px card sitting under full-width ones. */
    .t-cards, .t-cards tbody, .t-cards tfoot, .t-cards tr, .t-cards td { display: block; width: auto; }

    .t-cards tr {
        border: 1px solid var(--c-line, #e6e9ee);
        border-radius: 10px;
        margin-bottom: 10px;
        padding: 4px 2px;
        background: var(--c-surface, #fff);
    }

    .t-cards td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 14px;
        padding: 7px 11px;
        border: 0;
        text-align: right;
    }

    .t-cards td + td { border-top: 1px solid var(--c-line, #eef1f5); }

    .t-cards td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        text-align: left;
        font-size: .74rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: .4px;
        color: var(--muted, #6b7785);
    }

    /* A cell with no label prints an empty ::before and keeps its own alignment — used for the
       row's actions, which read better as a full-width strip than as a labelled value. */
    .t-cards td:not([data-label])::before { display: none; }
    .t-cards td:not([data-label]) { justify-content: flex-start; text-align: left; }

    /* The row's identity. Marked per table because only the page knows which column names the
       thing — it reads as the card's heading rather than as one more labelled field. */
    /* BLOCK, not the flex the other cells use. A heading cell often carries a second line under
       the name — Global Changes puts the category there — and as a flex row that subtitle lands
       beside the name, right-aligned, reading as a separate value. */
    .t-cards td.t-head {
        display: block;
        font-size: 1rem;
        font-weight: 700;
        color: var(--c-text, #22303c);
        text-align: left;
        padding-top: 10px;
    }

    .t-cards td.t-head::before { display: none; }

    /* An empty table still has to say so. Without this the "no results" row becomes a card with
       one blank line in it. */
    .t-cards td.t-empty { justify-content: center; text-align: center; color: var(--muted, #6b7785); }
    .t-cards td.t-empty::before { display: none; }

    /* A blank cell with no label has nothing to contribute — it is a spacer that exists to hold a
       column position, and there are no columns here. Left in, a footer row prints one empty line
       per non-summable column. Labelled blanks are KEPT: "Expires — (blank)" is a fact. */
    .t-cards td:not([data-label]):empty { display: none; }

    /* Row actions read as a strip along the bottom of the card rather than one full-width block
       each. inline-block keeps Edit and Delete on the same line at any sane button width. */
    /* `border: 0`, not `border-top: 0`. The legacy grid styles reach these cells through
       `.grid tbody td`, which is more specific than the plain `.t-cards td` reset above and was
       still drawing a hairline under the buttons. Two classes plus an element beats it. */
    .t-cards td.act {
        display: inline-block;
        width: auto;
        border: 0;
        padding: 8px 5px 10px;
    }
    .t-cards td.act:first-of-type { padding-left: 11px; }

    /* The footer total, which is a row like any other and should not read as one more record.
       The legacy `.total-row td` rule puts a 2px rule above each cell, which inside a card draws
       a line through the middle of it — the card's own border already separates it. */
    .t-cards tfoot tr { background: var(--c-s2, #f6f8fc); }
    .t-cards tfoot td, .t-cards tr.total-row td { border-top: 0; border-bottom: 0; }
    .t-cards tfoot td + td { border-top: 1px solid var(--c-line, #eef1f5); }

    /* The share bar keeps its bar. It is the one cell whose VALUE is the graphic, so it stacks
       under its label rather than sitting beside it — a 3mm-wide bar squeezed into the right-hand
       column of a card conveys nothing. */
    .t-cards td.rg-share { flex-wrap: wrap; }
    .t-cards td.rg-share .rg-share-b { flex: 1 0 100%; }

    /* A clickable report row has to look like one when it is a card. The grid's hover cue is a
       row highlight, which on a phone nobody ever sees. */
    .t-cards tr.hit { cursor: pointer; }

    /* EDITABLE cells. Scheduled Pricing puts a price field in the grid, so the cards have to be
       usable and not just readable. Bounded rather than full-width: a price box stretched across
       the card reads as a text field and invites a sentence. */
    .t-cards td input[type="number"],
    .t-cards td input[type="text"] { max-width: 46%; text-align: right; }
    .t-cards td input[type="checkbox"] { width: 22px; height: 22px; }
}

/* =============================================================================================
   Status messages — the ✅ / ⚠️ replacements.

   THE WHOLE POINT IS THAT COLOUR IS NOW OURS TO DECIDE. An emoji carries its own colour baked
   into the font: a warning triangle is yellow-and-black on Windows, flat grey on some Linux
   builds, Apple orange on macOS. That made the colour of a status indicator a property of the
   operating system rather than of whether anything was actually wrong — and on a screen that
   reports whether a ledger balances, that is not a cosmetic problem.

   These are stroked paths at currentColor, so the icon takes the colour of the message it sits
   in: --c-go-text when things are fine, --c-warn when they are not. One decision, one place.
   ============================================================================================= */

.st-msg { display: inline-flex; align-items: flex-start; gap: 8px; line-height: 1.5; }
.st-msg .bo-ico { width: 17px; height: 17px; flex: 0 0 auto; margin-top: 1px; }
.st-msg.ok   { color: var(--c-go-text); }
.st-msg.warn { color: var(--c-warn); }

/* The unmapped-tax-class banner sets its own colour on .sts-warn, so the icon only needs sizing
   and a baseline nudge — it inherits the rest. */
.sts-warn .bo-ico { width: 17px; height: 17px; vertical-align: -3px; margin-right: 4px; }

/* Close buttons. Sized here rather than in the component so each caller keeps control. */
.rd-x .bo-ico { width: 15px; height: 15px; }
#blazor-error-ui .dismiss .bo-ico { width: 14px; height: 14px; vertical-align: -2px; }

/* The access-denied padlock is the one decorative use, and it is big. */
.dn-ic .bo-ico { width: 44px; height: 44px; color: var(--c-text-faint); }

/* ---- Page-level error banner -------------------------------------------
   GLOBAL, deliberately. Twenty pages render <div class="mg-err">, but the rule
   lived only in ModifierGroups.razor.css - a SCOPED stylesheet, so it compiled
   to .mg-err[b-modifiergroups] and matched nothing anywhere else. Those pages
   were emitting the element and showing bare unstyled text, which reads as
   nothing at all: the message was there, it simply did not look like a message.

   Kept here rather than copied into twenty scoped files so the next page to use
   the class inherits it. The ModifierGroups copy still wins on its own page,
   being more specific, so nothing there changes.

   (Lost once already, to a wholesale overwrite of this file on 2026-08-10, which
   put all nineteen other pages straight back to unstyled text. If this block is
   in the way of an incoming version, move it - do not drop it.) */
.mg-err {
    background: #fbe4e2;
    border: 1px solid #e6a9a3;
    color: #8a1f18;
    padding: 9px 12px;
    border-radius: 8px;
    font-size: .85rem;
    font-weight: 600;
    margin: 0 6px 14px;
}

/* ---- Page-level advisory notes ------------------------------------------
   GLOBAL for the same reason .mg-err above is: a scoped stylesheet compiles to
   .rf-note[b-thatpage] and matches nothing anywhere else, so the next page to
   use the class would render bare unstyled text and look like it had said
   nothing at all.

   .rf-note states a condition the operator has to act on but which is not an
   error - "this store has no kitchen stations yet". .rf-warn sits under a form
   field and says what the change in it will actually do. Both are calmer than
   .mg-err on purpose: nothing has failed. */
.rf-note {
    background: #eef3fb;
    border: 1px solid #c9d8ef;
    color: #2f4a70;
    padding: 10px 13px;
    border-radius: 8px;
    font-size: .85rem;
    line-height: 1.5;
    margin: 0 6px 14px;
}

.rf-warn {
    background: #fdf3e3;
    border: 1px solid #e8d3a8;
    color: #8a5a12;
    padding: 7px 10px;
    border-radius: 8px;
    font-size: .78rem;
    line-height: 1.45;
}


/* =================================================================================================
   ALL REPORTS — the picker.

   Two columns: what to run on the left, what to run it over on the right. The filter used to be a
   band across the top of every report page; here it is not a filter over something already on
   screen, it is half the question, so it sits beside the other half.
   ================================================================================================= */
.rp-pick {
    display: grid;
    /* The right column is fixed because its contents are fixed — a date picker and a handful of
       selects do not benefit from extra width, whereas the report list does. */
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: 18px;
    align-items: start;
    padding: 0 6px 24px;
}

.rp-pick-l, .rp-pick-r {
    background: #fff;
    border: 1px solid #e3e8ef;
    border-radius: 14px;
    padding: 14px;
}

.rp-pick-r { position: sticky; top: 16px; }

.rp-pick-h {
    font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
    color: #6b7688; padding: 2px 4px 10px;
}
.rp-pick-h.sub { border-top: 1px solid #eef1f6; margin-top: 14px; padding-top: 14px; }

/* A radio in everything but name. It has to read as "chosen, not yet run" — the running happens
   on the button below — so the selected state is a filled panel rather than a page-nav highlight. */
.rp-opt {
    display: flex; flex-direction: column; gap: 2px;
    width: 100%; text-align: left; cursor: pointer;
    padding: 10px 12px; margin-bottom: 6px;
    border: 1px solid transparent; border-radius: 10px;
    background: transparent; font: inherit;
    transition: background-color .12s ease, border-color .12s ease;
}
.rp-opt:hover { background: #f6f8fb; }
.rp-opt.on { background: #eef1ff; border-color: #4f5bd5; }

.rp-opt-t { font-weight: 600; color: #1f2733; }
.rp-opt-d { font-size: .82rem; color: #6b7688; line-height: 1.4; }
.rp-opt.on .rp-opt-t { color: #3b45b8; }

/* Per-report extras. Stacked label-over-control, because the panel is narrow and a label beside a
   select in 340px leaves the select too small to read an item category in. */
.rp-xf { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.rp-xf label { font-size: .78rem; font-weight: 600; color: #4a5568; }
.rp-xf select, .rp-xf input {
    width: 100%; padding: 8px 10px; font: inherit; font-size: .88rem;
    border: 1px solid #d6dce6; border-radius: 8px; background: #fff; color: #1f2733;
}

/* A BOOLEAN extra-filter. .rp-xf sets width:100% on every input it contains, which would stretch a
   checkbox right across the panel, so the box takes its own size back here. The caption sits BESIDE
   it rather than above, which is why .rp-xf.chk drops the column gap the labelled fields need. */
.rp-xf.chk { gap: 6px; }
.rp-xf .rp-chk {
    display: flex; align-items: flex-start; gap: 9px; cursor: pointer;
    font-size: .84rem; font-weight: 600; color: #1f2733;
}
.rp-xf .rp-chk input {
    width: 16px; height: 16px; flex: 0 0 auto; margin: 1px 0 0; padding: 0;
    accent-color: #4f5bd5; cursor: pointer;
}
.rp-xf .rp-chk span { line-height: 1.4; }

.rp-view {
    width: 100%; margin-top: 14px; padding: 11px 14px;
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    border: none; border-radius: 10px; cursor: pointer;
    background: #4f5bd5; color: #fff; font: inherit; font-weight: 600;
}
.rp-view:hover:not(:disabled) { background: #4450c4; }
.rp-view:disabled { opacity: .45; cursor: default; }
.rp-view .bo-ico { width: 16px; height: 16px; }

/* Reset, under View report. Same width so the two read as one block, and outlined rather than
   filled so the eye still lands on the action above it — this one undoes work, and a second solid
   button beside the primary competes with it for the same glance.

   Tighter top margin than .rp-view has, because that one is separating itself from the filter
   controls above while this is pairing with the button directly over it. */
.rp-reset {
    width: 100%; margin-top: 8px; padding: 10px 14px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid #d3dceb; border-radius: 10px; cursor: pointer;
    background: transparent; color: #46506a; font: inherit; font-weight: 600;
}
.rp-reset:hover { background: #eef1f7; border-color: #c2cddf; }
.rp-reset:active { transform: translateY(1px); }

/* Register and cashier, above the shift list they narrow. Side by side on a wide filter panel and
   stacked when it is narrow: two half-width selects that wrap to one column are still readable,
   whereas two that shrink to fit are two truncated names. */
.rp-shift-narrow { display: flex; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
.rp-shift-narrow > select { flex: 1 1 9rem; min-width: 0; }

.rp-view-hint { margin: 8px 0 0; font-size: .8rem; color: #6b7688; text-align: center; }
.rp-view-hint.warn { color: #b4531f; }

/* The stacked filter bar — same component, one column. */
.rfb.stacked { display: block; padding: 0; border: none; background: none; box-shadow: none; }
.rfb.stacked > * + * { margin-top: 10px; }
.rfb.stacked .rfb-presets { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }

/* =================================================================================================
   THE DOCUMENT VIEW — toolbar above the paper.
   ================================================================================================= */
.rp-doc { padding: 0 6px 24px; }

.rp-toolbar {
    display: flex; align-items: center; gap: 14px;
    padding: 10px 12px; margin-bottom: 4px;
    background: #fff; border: 1px solid #e3e8ef; border-radius: 12px;
    /* FLUSH UNDER THE APP BAR, not at the top of the viewport. At top:0 it stuck to the same line
       the app bar holds and lost the z-index fight to it, so it slid out of sight and the whole
       page read as one long scroll. Flush rather than offset on purpose: any gap here is a window
       the report's own content scrolls through, between two bars that are meant to look joined. */
    position: sticky; top: var(--bo-top-h); z-index: 5;
}
.rp-back { text-decoration: none; color: #4f5bd5; font-weight: 600; white-space: nowrap; }
.rp-back:hover { text-decoration: underline; }

.rp-toolbar-t { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.rp-toolbar-t strong { color: #1f2733; }
.rp-toolbar-t span { font-size: .8rem; color: #6b7688; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rp-toolbar-a { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.rp-toolbar-a .bo-ico { width: 15px; height: 15px; vertical-align: -2px; }

.rf-head-a { display: flex; align-items: center; gap: 8px; }
.rf-head-a .bo-ico { width: 15px; height: 15px; vertical-align: -2px; }

@media (max-width: 1100px) {
    .rp-pick { grid-template-columns: 1fr; }
    .rp-pick-r { position: static; }
}


/* =================================================================================================
   THE ORDER SHEET — the transaction detail dialogue.

   The list was cut to six columns, so everything that came off it has to be findable here and
   comfortable to read. The layout is a receipt read top to bottom: who and what state at the top
   with the total beside it, the facts, then the tables, then the working.
   ================================================================================================= */
.ordv {
    width: min(980px, 96vw);
    max-height: 92vh;
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: 16px;
    box-shadow: 0 24px 60px rgba(15, 23, 42, .28);
    overflow: hidden;
}

/* The masthead is tinted so the body reads as paper against it — the eye needs one break between
   "which order is this" and "here are its numbers", and a rule alone was not enough at this width. */
.ordv-h {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 20px;
    padding: 18px 22px;
    background: linear-gradient(180deg, #f7f9fc, #eef2f8);
    border-bottom: 1px solid #dde4ee;
    flex: 0 0 auto;
}

.ordv-no { display: flex; align-items: baseline; gap: 8px; }
.ordv-no-k { font-size: .8rem; font-weight: 600; text-transform: uppercase; letter-spacing: .1em; color: #6b7688; }
.ordv-no-v { font-size: 1.65rem; font-weight: 700; color: #1f2733; font-variant-numeric: tabular-nums; }

.ordv-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ordv-chip {
    padding: 2px 10px; border-radius: 999px;
    background: #fff; border: 1px solid #d6dce6;
    font-size: .78rem; font-weight: 600; color: #4a5568;
}

.ordv-h-r { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; flex: 0 0 auto; }

/* The one figure most people opened this for. Given its own block at the top rather than left at
   the bottom of the working — a glance, not a search. */
.ordv-tot { display: flex; flex-direction: column; align-items: flex-end; }
.ordv-tot span { font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .1em; color: #6b7688; }
.ordv-tot b { font-size: 1.7rem; color: #1f2733; font-variant-numeric: tabular-nums; line-height: 1.1; }

.ordv-h-a { display: flex; align-items: center; gap: 8px; }
.ordv-h-a .bo-ico { width: 15px; height: 15px; vertical-align: -2px; }

.ordv-b { padding: 18px 22px 22px; overflow-y: auto; flex: 1 1 auto; }
.ordv-b h4 {
    margin: 22px 0 8px; font-size: .78rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .09em; color: #6b7688;
}
.ordv-b h4:first-of-type { margin-top: 0; }

/* Facts as a responsive grid of label-over-value. auto-fit rather than a fixed column count so the
   same block works at 980px and on a laptop without a second breakpoint. */
.ordv-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 12px 18px;
    margin: 0 0 4px;
    padding: 14px 16px;
    background: #f7f9fc;
    border: 1px solid #e8edf5;
    border-radius: 12px;
}
.ordv-facts > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ordv-facts dt { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: #8a93a5; }
.ordv-facts dd { margin: 0; font-size: .92rem; color: #1f2733; overflow-wrap: break-word; }

/* Void is not a sixth fact — it changes how every figure below should be read. */
.ordv-void {
    display: flex; align-items: flex-start; gap: 10px;
    margin-top: 12px; padding: 11px 14px;
    background: #fdf0ef; border: 1px solid #f3c9c4; border-radius: 10px;
    color: #a3352a; font-size: .88rem; line-height: 1.45;
}
.ordv-void .bo-ico { width: 17px; height: 17px; flex: 0 0 auto; margin-top: 1px; }

/* The working. Right-aligned and narrow, like the foot of a receipt. */
.ordv-sum {
    margin: 20px 0 0 auto;
    width: min(340px, 100%);
    border-top: 1px solid #e3e8ef;
    padding-top: 12px;
}
.ordv-sum > div { display: flex; justify-content: space-between; gap: 20px; padding: 4px 0; font-size: .92rem; color: #4a5568; }
.ordv-sum b { color: #1f2733; font-variant-numeric: tabular-nums; }
.ordv-sum .tot {
    margin-top: 6px; padding-top: 10px; border-top: 2px solid #1f2733;
    font-size: 1.05rem; font-weight: 700; color: #1f2733;
}
.ordv-sum .tot b { font-size: 1.2rem; }

/* Staged for the printer and never shown on screen. The print block below reveals it. */
.rp-printonly { display: none; }

/* =================================================================================================
   PRINTING A REPORT

   GLOBAL, and it has to be. The report paper styles itself in its own scoped stylesheet, but the
   things that must NOT print — the sidebar, the top bar, the toolbar above the preview — belong to
   the layout and to other components, and a scoped sheet cannot reach outside its own component.

   Written as "hide the shell" rather than "show only the paper" because the Back Office renders
   several fixed-position elements (the error strip, any open drawer) that are not descendants of
   the page at all; a display:none on the shell leaves them, so they are named too.
   ================================================================================================= */
@page {
    size: A4 portrait;
    /* The sheet draws its own margins, so the page box has none. Two sets of margins would inset
       the content twice and lose about 25mm of the usable width — which on a nine-column report is
       the difference between fitting and wrapping every row. */
    margin: 0;
}

@media print {
    /* The desk furniture. */
    .bo-side,
    .bo-top,
    #blazor-error-ui,
    .rp-print-hide {
        display: none !important;
    }

    html, body, .bo, .bo-main {
        background: #fff !important;
        margin: 0 !important;
        padding: 0 !important;
        /* The shell scrolls its own main region; leaving that in place prints one screenful and
           stops, which looks like the report has been truncated. */
        overflow: visible !important;
        height: auto !important;
        display: block !important;
    }

    .bo-content,
    .rp2-page,
    .rp2,
    .rp2-main {
        margin: 0 !important;
        padding: 0 !important;
        overflow: visible !important;
        display: block !important;
        max-width: none !important;
        width: auto !important;
    }

    /* The staged report document is the only thing that prints. */
    .rp-printonly { display: block !important; }

    /* Links print as text. A URL in brackets after every link is a browser default that turns a
       clean table into a mess of addresses nobody can follow off paper anyway. */
    a[href]::after { content: "" !important; }
}

/* ---- Date & Time Format ------------------------------------------------------------------
   Radio cards rather than two dropdowns. The choice is between things that LOOK different, so
   every option shows what it produces against the current instant — "dd/MM/yyyy" tells a manager
   nothing, "11/08/2026" tells them everything, and the two are indistinguishable for the first
   twelve days of every month if you only read the pattern. */
.df-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 18px; padding: 0 6px; }

.df-card { background: #fff; border: 1px solid #e3e8ef; border-radius: 14px; padding: 16px; }
.df-card h3 { margin: 0 0 12px; font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .09em; color: #6b7688; }

/* Radio in its own column, spanning both text rows, so the example and its description read as
   one block rather than the caption hanging under the control. */
.df-opt {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 2px 12px;
    padding: 11px 13px; margin-bottom: 6px;
    border: 1px solid #e8edf5; border-radius: 10px; cursor: pointer;
    transition: background-color .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.df-opt:hover { background: #f6f8fb; border-color: #d6dce6; }
.df-opt.on { background: #eef1ff; border-color: #4f5bd5; box-shadow: inset 0 0 0 1px #4f5bd5; }

.df-opt input { grid-column: 1; grid-row: 1 / span 2; align-self: center; width: 17px; height: 17px; accent-color: #4f5bd5; }

/* The example leads and the description explains it — the order a manager reads them in. Both are
   placed explicitly: auto-placement put the example in whichever cell happened to be free, which
   is fragile the moment a row gains an element. */
.df-eg {
    grid-column: 2; grid-row: 1;
    font-size: 1.02rem; font-weight: 700; color: #1f2733;
    font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.df-lb { grid-column: 2; grid-row: 2; font-size: .8rem; color: #6b7688; }

.df-preview {
    display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
    margin: 18px 6px 0; padding: 14px 16px;
    background: #eef1ff; border: 1px solid #cdd4f7; border-radius: 12px;
}
.df-preview span { color: #4a5568; font-size: .9rem; }
.df-preview strong { font-size: 1.15rem; color: #1f2733; font-variant-numeric: tabular-nums; }

.df-note {
    display: flex; align-items: flex-start; gap: 10px;
    margin: 14px 6px 0; padding: 12px 14px;
    background: #fffaf0; border: 1px solid #f0dcb4; border-radius: 10px;
    color: #7a5b1e; font-size: .85rem; line-height: 1.5;
}
.df-note .bo-ico { width: 17px; height: 17px; flex: 0 0 auto; margin-top: 1px; }
