﻿/*
    Theme: 70% gray, 20% teal, 10% gold.

    Gray is the ground: surfaces, borders, body text, the sidebar, table shapes. Teal is the
    action colour: primary buttons, links, the active tab / nav item, focus rings. Gold is the
    accent and it is scarce on purpose: the brand mark, the selected table on a room canvas, a
    badge that must be seen. If a screen has more than one gold thing on it, one of them is wrong.

    Every colour below is a variable. Screens use the variables (scoped .razor.css files can
    read them too); do not introduce new hues. Bootstrap's primary is remapped to teal here so
    .btn-primary, links and form focus follow the theme without per-component overrides.

    The ONE place more hues live is the chart palette (--pos-chart-*), because a chart with three
    lines needs three colours that can be told apart and the theme has two. They are declared
    there and nowhere else.
*/
/* HOW BIG THIS DEVICE DRAWS THE APP. --ytf-scale is one number per device, written onto <html>
   by js/displayScale.js before the first paint and stepped from the sidebar; unset it is 1 and
   nothing changes. It multiplies the ROOT FONT SIZE, so it redefines what a rem is on this
   device - and nearly everything in this app is a rem, or an em of one, so the text, the
   controls and the touch targets all come up together.

   It moves NO BREAKPOINT: a media query in px does not care what the root font size is, so a
   device keeps the layout it was designed for and only the size changes. That is the whole
   reason it is this and not a viewport override, which would have dragged the portrait drawer,
   the sidebar's collapse and both POS clamps along with it.

   100% and not 16px, so it MULTIPLIES whatever the browser is already set to rather than
   overruling somebody who has turned their own text up. */
html {
    font-size: calc(100% * var(--ytf-scale, 1));
}

:root {
    --pos-gray-50: #f7f8f9;
    --pos-gray-100: #eef0f2;
    --pos-gray-200: #dfe3e7;
    --pos-gray-300: #c8ced4;
    --pos-gray-400: #a3abb3;
    --pos-gray-500: #7c858e;
    --pos-gray-600: #5f6870;
    --pos-gray-700: #464d54;
    --pos-gray-800: #30353a;
    --pos-gray-900: #202428;

    --pos-teal: #12808a;
    --pos-teal-rgb: 18, 128, 138;
    --pos-teal-dark: #0e666e;
    --pos-teal-light: #d6eef0;

    --pos-gold: #d19a1a;
    --pos-gold-rgb: 209, 154, 26;
    --pos-gold-dark: #a97a10;
    --pos-gold-light: #fbf0d2;

    /* Red, for what is OFF or has gone wrong: a dish out of stock, a card gone critical, money
       handed back. Three reds were typed by hand for those, plus Bootstrap's own danger; they are
       one family now, and Bootstrap's danger is remapped onto it below as its primary is onto teal. */
    --pos-red: #c0392b;
    --pos-red-rgb: 192, 57, 43;
    --pos-red-dark: #a12a2a;
    --pos-red-light: #fdecec;
    --pos-red-border: #e6a9a2;

    /* THE CHART PALETTE. A chart's series take these IN ORDER - its first series slot 1, its
       second slot 2 - and never cycle: a fifth series folds into "Other" or becomes a second
       chart, never a made-up colour. A chart reads them by name through ChartTheme, and the
       Chart.js interop resolves var(...) when it draws, so no colour code is written anywhere else.

       The theme's own teal and gold could not do this job. Checked as a set, the brand teal is
       too muted to read as a colour beside a line of gray, the gold falls below 3:1 against a
       white card, and a gray series reads as the chart's own grid. These are a teal and a gold
       stepped up until they pass, then a violet and a blue for the third and fourth, and the four
       were checked together for lightness, strength of colour, colour-blind separation of
       neighbours and 3:1 on white. Re-check the set before changing any one of them.

       Red is not here: it is kept for what is off or has gone wrong, and a series coloured red
       reads as a problem. */
    --pos-chart-1: #0d9488;   /* teal */
    --pos-chart-2: #c98500;   /* amber */
    --pos-chart-3: #4a3aa7;   /* violet */
    --pos-chart-4: #2a78d6;   /* blue */
    /* The same four as numbers, for a shaded band behind a line, which wants its colour at a
       fraction of its strength: rgba(var(--pos-chart-1-rgb), .15). Change a colour, change both. */
    --pos-chart-1-rgb: 13, 148, 136;
    --pos-chart-2-rgb: 201, 133, 0;
    --pos-chart-3-rgb: 74, 58, 167;
    --pos-chart-4-rgb: 42, 120, 214;
    --pos-chart-grid: var(--pos-gray-200);
    --pos-chart-ink: var(--pos-gray-500);

    /* A bill's five columns: quantity, name, kitchen mark, per-unit money, line total. Shared by
       BillLine and by BillView's charge rows, which are separate components and so separate CSS
       scopes - one definition here is what keeps a subtotal's amount in the same column as the
       totals it is summing.

       THE MONEY COLUMNS ARE FIXED, and that is the whole point of them. Every line is its own grid
       container, so an `auto` column is measured PER LINE: a bill holding a 16.40 beside a 4.10 gave
       column 5 two different widths, the 1fr name column absorbed the difference, and the per-unit
       price moved sideways from row to row while the totals stayed put. Columns can only line up
       across lines if they do not depend on what is in them.

       FIXED IS NOT THE SAME AS ROOMY, and they were roomy: sized for 1234.50, a figure this shop
       will never ring up on one line, the two money columns took most of the width of the till's
       order panel and cut the dish name - the one part a cashier reads to check a bill against the
       table - down to four characters. They now hold 999.90 and their gutters are narrower. A
       number that does not fit leans LEFT into its gutter rather than widening the column, because
       every cell in them is right-aligned with `min-width: 0`: the right-hand edge stays where it
       is on every other line and only the one big number comes closer to the name. */
    --bill-cols: 2.1em 1fr auto 3.1em 3.8em;

    --bs-primary: var(--pos-teal);
    --bs-primary-rgb: var(--pos-teal-rgb);
    --bs-link-color: var(--pos-teal);
    --bs-link-color-rgb: var(--pos-teal-rgb);
    --bs-link-hover-color: var(--pos-teal-dark);
    --bs-link-hover-color-rgb: 14, 102, 110;
    --bs-body-color: var(--pos-gray-800);
    --bs-body-color-rgb: 48, 53, 58;
    --bs-secondary-color: var(--pos-gray-500);
    --bs-border-color: var(--pos-gray-200);

    --bs-danger: var(--pos-red);
    --bs-danger-rgb: var(--pos-red-rgb);
    --bs-danger-text-emphasis: var(--pos-red-dark);
    --bs-danger-bg-subtle: var(--pos-red-light);
    --bs-danger-border-subtle: var(--pos-red-border);
}

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* Panning and nothing else. This is the half of "pinch-to-zoom is off" that CSS can state (see
   js/noZoom.js for the rest): allowing only the two pans rules out the pinch AND the double-tap
   zoom, and takes the tap delay off every button on the way, because a browser that cannot
   double-tap-zoom has nothing to wait for. A component that wants a finger to do something else
   narrows this further on its own element - the room canvas and the photo cropper both say
   touch-action: none - which is allowed. Nothing can widen it back. */
html {
    touch-action: pan-x pan-y;
}

a, .btn-link {
    color: var(--pos-teal);
}

.btn-primary {
    --bs-btn-bg: var(--pos-teal);
    --bs-btn-border-color: var(--pos-teal);
    --bs-btn-hover-bg: var(--pos-teal-dark);
    --bs-btn-hover-border-color: var(--pos-teal-dark);
    --bs-btn-active-bg: var(--pos-teal-dark);
    --bs-btn-active-border-color: var(--pos-teal-dark);
    --bs-btn-disabled-bg: var(--pos-teal);
    --bs-btn-disabled-border-color: var(--pos-teal);
}

.btn-outline-primary {
    --bs-btn-color: var(--pos-teal);
    --bs-btn-border-color: var(--pos-teal);
    --bs-btn-hover-bg: var(--pos-teal);
    --bs-btn-hover-border-color: var(--pos-teal);
    --bs-btn-active-bg: var(--pos-teal-dark);
    --bs-btn-active-border-color: var(--pos-teal-dark);
}

.btn-danger {
    --bs-btn-bg: var(--pos-red);
    --bs-btn-border-color: var(--pos-red);
    --bs-btn-hover-bg: var(--pos-red-dark);
    --bs-btn-hover-border-color: var(--pos-red-dark);
    --bs-btn-active-bg: var(--pos-red-dark);
    --bs-btn-active-border-color: var(--pos-red-dark);
    --bs-btn-disabled-bg: var(--pos-red);
    --bs-btn-disabled-border-color: var(--pos-red);
}

.btn-outline-danger {
    --bs-btn-color: var(--pos-red);
    --bs-btn-border-color: var(--pos-red);
    --bs-btn-hover-bg: var(--pos-red);
    --bs-btn-hover-border-color: var(--pos-red);
    --bs-btn-active-bg: var(--pos-red-dark);
    --bs-btn-active-border-color: var(--pos-red-dark);
    --bs-btn-disabled-color: var(--pos-red);
    --bs-btn-disabled-border-color: var(--pos-red);
}

.btn-outline-secondary {
    --bs-btn-color: var(--pos-gray-700);
    --bs-btn-border-color: var(--pos-gray-300);
    --bs-btn-hover-bg: var(--pos-gray-100);
    --bs-btn-hover-border-color: var(--pos-gray-400);
    --bs-btn-hover-color: var(--pos-gray-900);
    --bs-btn-active-bg: var(--pos-teal-light);
    --bs-btn-active-border-color: var(--pos-teal);
    --bs-btn-active-color: var(--pos-teal-dark);
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-select:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem rgba(var(--pos-teal-rgb), .6);
}

.form-control:focus, .form-select:focus {
    border-color: var(--pos-teal);
}

.form-check-input:checked {
    background-color: var(--pos-teal);
    border-color: var(--pos-teal);
}

.nav-tabs {
    --bs-nav-tabs-link-active-color: var(--pos-teal-dark);
    --bs-nav-link-color: var(--pos-gray-600);
    --bs-nav-link-hover-color: var(--pos-teal);
}

.nav-tabs .nav-link.active {
    font-weight: 600;
    box-shadow: inset 0 2px 0 var(--pos-teal);
}

/*
    Bootstrap components whose active colour is a LITERAL blue on their own component variables
    rather than a read of --bs-primary, so the remap at the top never reaches them: the
    list-group's selected row (the shop settings subjects, the modifier and schedule lists), the
    pagination's current page, a range slider's thumb, a switch's focus glyph, a checkbox's
    focus border and the table row a drag is over. Each is remapped here, where the theme lives,
    rather than by editing Bootstrap or the page that happened to show it first.
*/
.list-group {
    --bs-list-group-active-bg: var(--pos-teal);
    --bs-list-group-active-border-color: var(--pos-teal);
    --bs-list-group-action-active-bg: var(--pos-gray-200);
}

.pagination {
    --bs-pagination-color: var(--pos-teal);
    --bs-pagination-hover-color: var(--pos-teal-dark);
    --bs-pagination-focus-color: var(--pos-teal-dark);
    --bs-pagination-focus-box-shadow: 0 0 0 .25rem rgba(var(--pos-teal-rgb), .25);
    --bs-pagination-active-bg: var(--pos-teal);
    --bs-pagination-active-border-color: var(--pos-teal);
}

.table-primary {
    --bs-table-color: var(--pos-teal-dark);
    --bs-table-bg: var(--pos-teal-light);
    --bs-table-border-color: var(--pos-teal);
    --bs-table-striped-bg: var(--pos-teal-light);
    --bs-table-striped-color: var(--pos-teal-dark);
    --bs-table-active-bg: var(--pos-teal-light);
    --bs-table-active-color: var(--pos-teal-dark);
    --bs-table-hover-bg: var(--pos-teal-light);
    --bs-table-hover-color: var(--pos-teal-dark);
}

.form-check-input:focus {
    border-color: var(--pos-teal);
}

.form-switch .form-check-input:focus {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%2818, 128, 138, 0.6%29'/%3e%3c/svg%3e");
}

.form-range::-webkit-slider-thumb {
    background-color: var(--pos-teal);
}

.form-range::-moz-range-thumb {
    background-color: var(--pos-teal);
}

.form-range::-webkit-slider-thumb:active {
    background-color: var(--pos-teal-light);
}

.form-range::-moz-range-thumb:active {
    background-color: var(--pos-teal-light);
}

.form-range:focus::-webkit-slider-thumb {
    box-shadow: 0 0 0 1px #fff, 0 0 0 .25rem rgba(var(--pos-teal-rgb), .25);
}

.form-range:focus::-moz-range-thumb {
    box-shadow: 0 0 0 1px #fff, 0 0 0 .25rem rgba(var(--pos-teal-rgb), .25);
}

/* The dock's one accent - the active tab's underline and the drop indicator - is a variable the
   library exposes for exactly this, so the host never reaches into its stylesheet. */
.dock-host {
    --dock-accent: var(--pos-teal);
    --dock-accent-rgb: var(--pos-teal-rgb);
    /* Each group is a box (FreePanel draws it), the same box as a table's (ft-box below): the
       takings line's border, and its shade under the headers and tabs. */
    --dock-box-border: var(--pos-gray-300);
    --dock-box-radius: .375rem;
    --dock-chrome-bg: var(--pos-gray-100);
    --dock-rule: var(--pos-gray-300);
}

/* FreeCalendar the same way, for the span picker and the month calendar alike: the picked days in
   teal, the days between them in the light teal, and the theme's grays for everything quiet. A page
   whose items mean something else colours them itself (the Calendar page, each kind of entry its own). */
.fc-range,
.mc {
    --fc-accent: var(--pos-teal);
    --fc-band: var(--pos-teal-light);
    --fc-hover: var(--pos-gray-100);
    --fc-border: var(--pos-gray-200);
    --fc-muted: var(--pos-gray-500);
    --fc-off: var(--pos-gray-100);
    --fc-item: var(--pos-teal);
}

/* ---- The common reasons, as one tap each (the void and return dialogs) ----
   A wrapped row of chips that stay readable rather than being squeezed onto one line, because a
   reason nobody can read is a reason nobody taps. */
.reason-chips {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
}

.reason-chips .btn {
    min-height: 2.4rem;
}

/* ---- A split-panel page (Components/Shared/SplitPanelPage draws the frame) ----
   The page fills the layout's article - a flex column - so its panes get the viewport height and
   each scrolls on its own; the body clips, so a pane scrolls inside itself rather than taking the
   page with it; the properties pane sits a shade off white, apart from the list. Seven
   stylesheets each carried these three rules under names of their own. */
.split-page {
    flex: 1 1 auto;
    min-height: 0;
}

.split-body {
    min-height: 0;
    overflow: hidden;
}

.split-props {
    background: var(--pos-gray-50);
}

/* ---- A table in a box (FreeTable's ft-box, which the list shells and TableBeside draw) ----
   The same box as the others on a page - the takings line on Bill history is the model: the
   border, the corners, and the shaded band, here under the column headings and behind the pager -
   so a list reads as one of the page's boxes rather than bare lines between them. */
:root {
    --ft-box-border: var(--pos-gray-300);
    --ft-box-radius: .375rem;
    --ft-head-bg: var(--pos-gray-100);
    --ft-foot-bg: var(--pos-gray-100);
}

/* ---- Drag to reorder (FreeTable's RowReorder and ReorderGrip) ----
   The library draws the grip, hides it while a list cannot be reordered, and names the row a drop
   would land on; this is how they LOOK here. The grip is muted, and takes the row's own colour once
   the row is selected. The row a drop would land on in a LIST carries a teal line along its top; a
   table's row lights up with table-primary instead (its RowReorder names that class).
   Here and not in each page's stylesheet: the grip is the library's markup, which a page's scoped
   rules cannot reach, and the four copies had already drifted apart. */
.ft-grip {
    color: var(--pos-gray-500);
}

.active .ft-grip {
    color: inherit;
}

.ft-drop-target {
    box-shadow: inset 0 3px 0 var(--pos-teal);
}

/*
    Clickable text inside a table cell (e.g. the Name column of an admin list).

    Bootstrap's .btn hard-codes `text-align: center`, and an inline-block button
    shrink-to-fits to the column once its label is too long — so the moment a name
    wraps to a second line every line comes out centred instead of following the
    cell's own alignment. This resets the button back to behaving like ordinary cell
    text: it inherits the column's alignment (so it still right-aligns in a .text-end
    column), sits on the text baseline, and carries no button box.

    Use this for any in-table clickable label rather than re-deriving the
    `p-0 text-decoration-none align-baseline` class soup at each call site.
*/
.cell-link.btn {
    padding: 0;
    text-align: inherit;
    vertical-align: baseline;
    white-space: normal;
    text-decoration: none;
}

/*
    A row's action button - Edit, Deactivate, Open, Reprint, the buttons at the end of a table
    row. One height whether the label is one language or two and whether or not it carries a
    glyph, one minimum width so a column of them lines up, and the glyph BESIDE a stacked pair
    rather than wrapped above it (a bare .btn is inline-block, so in a 1%-width cell the glyph
    and the pair broke onto two lines). Every table's action cell uses this, and the gap between
    two of them comes from the sibling rule rather than a me-1 on each call site - which also
    left a trailing margin on a row whose last button was hidden.
*/
/*
    A button whose label is a bilingual PAIR, which on the admin screens is nearly all of them.

    The pair is an inline-flex box aligned on its FIRST line's baseline, so inside an inline-block
    .btn the second language hangs below the line box: about 6px of padding above it and NONE
    below. On a filled button nothing shows, because the fill simply carries on; on an OUTLINE
    button the border is drawn straight through the descenders of the second line, which reads as
    a label falling out of its button. Same markup either way - it is the border that tells on it.

    .row-action and .dialog-action are this same fix made twice already, for a table row and for a
    dialog footer. This is the third place it was needed, so the reason lives here once. Those two
    stay exactly as they are: they tie with this rule on specificity and come after it, so what
    they add on top - a touch-sized minimum, their own gap - still wins.

    The gap is load-bearing. Flex drops the whitespace-only text node between a glyph and its
    label, so without it the two would touch.

    A component that has chosen its own layout has to outrank this: .pos-action-btn stacks its
    glyph ABOVE its label, and says so by spelling itself .btn.pos-action-btn.
*/
.btn:has(.t) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
}

.btn.row-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    min-width: 4.5rem;
    min-height: 2.5rem;
    padding: .25rem .6rem;
    white-space: nowrap;
    vertical-align: middle;
}

.row-action + .row-action {
    margin-left: .25rem;
}

/*
    A dialog footer's button - the answers of a confirm, Cancel beside Send back, Close beside
    Reprint. Its sibling is .row-action and the reason is the same: a bare .btn is inline-block,
    so its height is only ever whatever its content came to, and a row where one button carries
    a glyph and the next does not came out as buttons of two different heights. Here they are
    one box: one minimum height and width, the glyph BESIDE the label, and the gap from the rule
    rather than a me-1 at each call site.

    The difference from .row-action is that a dialog label WRAPS. A footer has a card's width to
    share between three or four answers, so a bilingual pair drops onto two lines as a matter of
    course, and the minimum height is what a finger wants rather than what the text came to.

    Every button in a footer takes it, not only the ones with a glyph: the point is that they
    match. A footer whose buttons carry no glyph at all is already even and needs nothing.
*/
.btn.dialog-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    min-height: 3rem;
    min-width: 5.5rem;
    padding: .4rem .9rem;
}

/*
    A glyph-only button on the till: the X that closes an order's panel and the bill's tools menu
    beside it, the X of a split and of a draft, and the X that takes a discount off a bill. A
    square a finger can hit, and smaller than a stepper, because up to seven of them sit across an
    order's header in a panel that may be a third of a tablet.

    It was OrderScreen's scoped rule, so the adjustment dialog's X - the same class in another
    component - came out as a plain .btn, wider than it was tall, and nothing said so. Spelled
    .btn.pos-icon-btn for the reason the two above are: to outrank a bare .btn.
*/
.btn.pos-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.35rem;
    min-height: 2.35rem;
    padding: .25rem;
    font-size: 1rem;
}

.content {
    padding: 0;
    display: flex;
    flex-grow: 1;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

/* ---- Slip pictures (Hardware > Slip images) -------------------------------

   A strip of paper to judge a picture against. The picture inside is sized as a PERCENTAGE of
   this strip, never to fit its container, so what the page shows is the picture's true share of
   the roll - which is one of the only two things anybody comes to this page to find out. */
.slip-image-paper {
    max-width: 32rem;
    padding: .5rem;
    background: #fff;
    border: 1px solid var(--pos-gray-300);
    border-radius: 2px;
}

.slip-image-dots {
    display: block;
    height: auto;
    /* CENTRED, because that is where the test slip puts it - PrinterJobs.PictureTest sends
       ESC a 1 - and the whole point of showing a picture against a strip of paper is that the
       strip does not disagree with the roll. Left-aligned here while centred there was the
       preview telling a small lie about placement, which is the one thing it must never do. */
    margin-left: auto;
    margin-right: auto;
    /* Dots are meant to be SEEN as dots. Smoothing would hide exactly the roughness somebody is
       checking for before they let a logo onto every bill the shop prints. */
    image-rendering: pixelated;
}

.artwork-drop {
    padding: 1rem;
    text-align: center;
    border: 2px dashed var(--pos-gray-400);
    border-radius: .5rem;
}

.artwork-drop.is-over {
    border-color: var(--pos-teal);
    background: rgba(var(--pos-teal-rgb), .08);
}

/* ---- Reports (Sales > Report, Sales > Itemised report) ---------------------------------------
   Shared by both reports, which are two ways of reading the same trading and should look it. */

/* The report's dock is pulled out to the page's own edges (SalesReportPage.razor.css) so that a
   tab's CONTENT can carry the same inset as the head above it and line up with the title. The tab
   strip is padded back in by that same amount, so the tabs line up with it too. The dock's own
   stylesheet is global, which is how a host reaches its chrome; a page's scoped CSS cannot, because
   the dock's markup never carries the page's scope attribute. */
.report-dock .dock-tabstrip {
    padding-inline: 1.5rem;
}

/* Label left, amount right, one row each - the shape a till roll is read in. Tabular figures so
   the column of amounts lines up on the decimal point without a table. */
.report-rows {
    display: grid;
    grid-template-columns: 1fr auto;
    row-gap: .35rem;
    column-gap: 1rem;
    margin: 0;
}

.report-rows dt {
    font-weight: 400;
    color: var(--pos-gray-700);
}

.report-rows dd {
    margin: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Money a cashier took off or put on by hand. Darker than the rows around it, because this is the
   one block on the report where money left a bill because somebody decided it should - every other
   figure here is the arithmetic doing what it was set up to do. */
.report-rows dt.report-adjustment,
.report-rows dd.report-adjustment {
    color: var(--pos-gray-900);
    font-weight: 500;
}

/* How many bills it covered, beside its name: the figure that tells one big discount from many
   small ones, which the amount on its own cannot. */
.report-adjustment-bills {
    margin-left: .4em;
    font-size: .85em;
    font-weight: 400;
    color: var(--pos-gray-500);
}

/* The rows that carry a table's total on to another figure - the order types to net sales, net
   sales to the takings - held to the right under the figures like the totals on an invoice.
   Across a wide card a label and its amount were a monitor apart. */
.report-rows.report-bridge {
    max-width: 26rem;
    margin-left: auto;
}

/* The line the eye should land on: the total of each block. */
.report-strong {
    font-weight: 700;
    border-top: 2px solid var(--pos-gray-300);
    padding-top: .35rem;
    margin-top: .15rem;
}

.report-table td,
.report-table th {
    font-variant-numeric: tabular-nums;
}

.report-table tfoot td {
    border-top: 2px solid var(--pos-gray-300);
    font-weight: 700;
}

/* ---- The customer display's connection cover (App.razor, js/displayWatchdog.js) -------------

   What the second screen shows while its circuit is down: BLACK, the same "nothing on it" the
   display shows between customers, and never a message a customer has to read. Two elements
   wear it - Blazor's reconnect dialog, which it hands to us when an element with that id exists,
   and the error UI it shows when a circuit dies of an exception - and both exist on /display only.

   A THREE-SECOND GRACE before it appears, because the first ten reconnect attempts are immediate
   and an ordinary wifi blip is over before anybody could notice. It covers a bill as well as the
   rotation, on purpose: a payment screen whose circuit has gone is showing numbers that may
   already be wrong. The one dot in the corner says the screen is waiting rather than switched
   off, for whoever walks round to look. */
.display-cover {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: #000;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .6s ease, visibility 0s linear .6s;
}

.display-cover::after {
    content: "";
    position: absolute;
    right: 3vmin;
    bottom: 3vmin;
    width: 1.4vmin;
    height: 1.4vmin;
    border-radius: 50%;
    background: var(--pos-gray-600);
    animation: display-cover-wait 2.4s ease-in-out infinite;
}

@keyframes display-cover-wait {
    50% { opacity: .2; }
}

/* Every state but "hide" is a circuit that is not there. Blazor adds exactly one of these at a
   time, and "retrying" rides on top of "show". */
#components-reconnect-modal.display-cover:is(.components-reconnect-show, .components-reconnect-retrying,
    .components-reconnect-paused, .components-reconnect-failed, .components-reconnect-rejected,
    .components-reconnect-resume-failed) {
    opacity: 1;
    visibility: visible;
    transition: opacity .6s ease 3s, visibility 0s linear 3s;
}

/* No grace for a dead circuit: it is not coming back on its own, and Blazor toggles this one
   with an inline display, which a transition cannot start from. The watchdog reloads it. */
#blazor-error-ui.display-cover {
    opacity: 1;
    visibility: visible;
}

/* ---- Small pieces several screens draw ----------------------------------------------------- */

/* A price that no longer applies, beside the one that does: a line's menu price under the price the
   cashier set, a tile's everyday price under a channel's, a bill's total before its discount. Five
   stylesheets wrote this; each keeps only its own size and spacing. */
.price-was {
    color: var(--pos-gray-500);
    text-decoration: line-through;
    font-variant-numeric: tabular-nums;
}

/* How many things are behind a row or a tab - a category's dishes, a room's tables. The row or tab
   that is chosen says how it looks when chosen. */
.count-pill {
    background: var(--pos-gray-200);
    color: var(--pos-gray-700);
    font-weight: 500;
}

/* A search box with its magnifier inside it. Here and not in a page's stylesheet because the glyph
   is SvgIcon's markup, which a scoped rule reaches only through ::deep - the customer page's once
   went without, and its magnifier sat outside the box. Four pages drew this with three offsets; a
   page says only how wide its box is and, for a bigger box, how big. */
.search-box {
    position: relative;
}

.search-box > input {
    padding-left: 2.4rem;
}

.search-box > .search-box-glyph {
    position: absolute;
    left: .8rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--pos-gray-500);
    pointer-events: none;
}

/* The figure a numpad is typing - a discount, a line's price, the cash handed over - and the grey
   it is drawn in before anything is keyed, when it is a placeholder rather than a choice. Each
   dialog sets how big. */
.pad-figure {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.pad-figure.muted {
    color: var(--pos-gray-400);
}
