/* ============================================================================
   SyncLog — Soft-UI design system
   ----------------------------------------------------------------------------
   Every dashboard page includes this one file through includes/layout/header.php,
   so the look is controlled here rather than page by page.

   The theme is a warm cream ground carrying softly raised surfaces and a single
   deep teal accent. Depth is drawn with diffuse shadow rather than a hard 1px
   bevel: light falls from the top-left, so a raised thing catches a white edge
   along its top and drops a warm shadow below, and a recessed thing (a field,
   a well) does the reverse.

   Soft UI has one well-known failure: when depth is the only signal, an input
   stops looking like an input and contrast collapses into mush. Three rules
   keep that from happening here.

     1. Every control keeps a real 1px border as well as its shadow, so it still
        reads as a control on a flat background or in forced-colours mode.
     2. Every text pairing is checked to 4.5:1 against the surface it sits on.
     3. Colour is never the only carrier of meaning — a state has a word, a sign
        or a shape as well as a hue.

   Fonts are the ones the machine already ships. Nothing is downloaded, so the
   shop PC renders correctly with no network.
   ========================================================================== */

:root {
    /* --- Surfaces ------------------------------------------------------
       A single warm family, ordered light to dark. Raised things are lighter
       than the ground; recessed things are darker. */
    --desk: #e9e2d4;          /* the ground everything sits on */
    --face: #f6f2e9;          /* base layer — cards, bars */
    --face-raised: #fbf8f2;   /* raised layer — tiles, menus, hovered rows */
    --face-inset: #ebe4d6;    /* inset layer — wells, striped rows */
    --face-pressed: #ded4c2;  /* pressed layer — held buttons, selected chips */
    --field: #fdfbf7;         /* editable field */

    /* --- Edges ---------------------------------------------------------
       The hairline that survives when shadows are switched off. */
    --line: #ddd4c2;
    --line-strong: #c8bda6;
    --highlight: rgba(255, 255, 255, 0.92);

    /* --- Ink -----------------------------------------------------------
       Checked against --face: 13.1:1, 6.7:1 and 5.1:1. White on the accent
       is 6.9:1. */
    --ink: #2b2721;
    --ink-soft: #5b5346;
    --ink-mute: #6f6555;
    --ink-invert: #fdfbf7;

    /* --- Accent --------------------------------------------------------
       One teal, used for the primary action, the current destination and the
       focus ring. Nothing else competes for it. */
    --accent: #12655f;
    --accent-dark: #0d4b47;
    --accent-bright: #17817a;
    --accent-wash: #dceae7;   /* tinted surface, e.g. a selected row */
    --accent-ink: #0d4b47;    /* accent as text on --face — 8.4:1 */

    /* --- Semantic ------------------------------------------------------ */
    --red: #a33a34;
    --red-dark: #832a26;
    --red-wash: #f5e4e2;
    --green: #3c7a52;
    --green-dark: #2e6040;
    --green-wash: #e2eee5;
    --amber: #8a6216;
    --amber-wash: #f6ecd6;

    /* --- Elevation -----------------------------------------------------
       Five steps, matching the five things that can be above the page: flat,
       hover, raised, modal, popover. The shadow is warm rather than grey,
       because a neutral shadow over cream reads as dirt. */
    --e1: 0 1px 2px rgba(122, 104, 76, 0.10);
    --e2: 0 2px 6px rgba(122, 104, 76, 0.12), 0 1px 2px rgba(122, 104, 76, 0.08);
    --e3: 0 4px 14px rgba(122, 104, 76, 0.14), 0 1px 3px rgba(122, 104, 76, 0.08);
    --e4: 0 14px 36px rgba(122, 104, 76, 0.22), 0 2px 8px rgba(122, 104, 76, 0.10);
    --e5: 0 22px 52px rgba(122, 104, 76, 0.26), 0 4px 12px rgba(122, 104, 76, 0.12);
    /* The white top edge that makes a surface read as lit from above. */
    --lit: inset 0 1px 0 var(--highlight);
    /* Recessed: the shadow falls inside, from the top-left. */
    --inset: inset 0 2px 4px rgba(122, 104, 76, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.65);
    --inset-deep: inset 0 3px 7px rgba(122, 104, 76, 0.22);

    /* --- Radius scale --------------------------------------------------- */
    --r-xs: 4px;
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 16px;
    --r-xl: 24px;
    /* Badges only — status words, counts, title notes. Controls are square-
       cornered on --r-sm, so shape alone separates what you read from what
       you press. */
    --r-pill: 999px;

    /* --- Spacing scale -------------------------------------------------- */
    --s-1: 4px;
    --s-2: 8px;
    --s-3: 12px;
    --s-4: 16px;
    --s-5: 24px;
    --s-6: 32px;
    --gap: 16px;

    /* --- Metrics -------------------------------------------------------- */
    /* These three follow the type scale: a control has to stay taller than
       the text inside it, and the rail has to stay wider than its longest
       destination name. */
    --tap: 46px;              /* minimum comfortable click target */
    --rail: 248px;            /* navigation rail width */
    --head: 64px;             /* app header height */

    /* --- Motion ---------------------------------------------------------
       Two durations: one for a thing changing under the pointer, one for a
       thing arriving on screen. Both are switched off wholesale further down
       when the operator has asked for reduced motion. */
    --t-fast: 120ms;
    --t-base: 200ms;
    --ease: cubic-bezier(0.2, 0.8, 0.3, 1);

    --font: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system,
            "Helvetica Neue", Arial, sans-serif;

    /* --- Type scale -----------------------------------------------------
       Ten steps, and nothing in the sheet sets a size any other way. The
       comment after each one is what it comes to on a browser left at its
       default, but the unit is deliberately rem rather than px: an operator
       who has raised the text size in Windows or in the browser gets a
       bigger screen everywhere, which a px size would ignore.

       The ladder starts a shade over 14px. Below that a shop PC at arm's
       length under fluorescent light is guesswork, so a column heading or a
       unit label is small relative to the row it sits on but still plainly
       readable.

       Every step is exactly 1.2x what it was when the ladder was first drawn:
       the shop read the first pass and asked for another twenty per cent, and
       lifting the whole ladder by one multiplier keeps the proportions between
       the steps intact. The metrics below follow from it — a control has to
       stay taller than its text.

       To make the whole system larger or smaller, move these ten numbers —
       every size on every page follows. */
    --fs-micro: 0.9rem;      /* 14.4px — uppercase micro labels, tallies */
    --fs-tiny: 0.975rem;     /* 15.6px — column headings, sub-lines, hints */
    --fs-small: 1.05rem;     /* 16.8px — secondary text, small buttons */
    --fs-base: 1.125rem;     /* 18px — body, table cells, buttons, nav */
    --fs-md: 1.2rem;         /* 19.2px — card titles, emphasised figures */
    --fs-lg: 1.35rem;        /* 21.6px — section headings, totals */
    --fs-xl: 1.5rem;         /* 24px — a figure that carries a screen */
    --fs-2xl: 1.8rem;        /* 28.8px — modal and page titles */
    --fs-3xl: 2.1rem;        /* 33.6px — tile readings on a phone */
    --fs-4xl: 2.4rem;        /* 38.4px — dashboard readings, landing title */
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    margin: 0;
    background: var(--desk);
    color: var(--ink);
    font-family: var(--font);
    font-size: var(--fs-base);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

/* The shell is a column: header, workspace, status bar. Only the workspace
   scrolls, so the navigation and the counts never leave the screen. */
body.has-shell {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
}

/* --- Depth helpers ---------------------------------------------------------
   `raised` sits above the ground. It keeps a hairline border, so the shape
   survives without the shadow. */
.raised {
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e2), var(--lit);
}

/* The `hidden` attribute means hidden, whatever else is said about display.
   The browser's own rule for it is only `display: none` at user-agent weight,
   so ANY author rule that sets display — and most things here are flex — beats
   it and the element stays on screen. Scripts throughout this app hide rows,
   tiles and list options by setting `.hidden`; without this they were setting
   an attribute that did nothing, which is exactly what had quietly broken the
   product search in the walk-in register. */
[hidden] { display: none !important; }

/* Skip link — the first tab stop, hidden until focused. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 200;
    background: var(--accent);
    color: var(--ink-invert);
    padding: 10px 16px;
    border-radius: var(--r-sm);
    font-weight: 600;
    text-decoration: none;
}

.skip-link:focus { left: 8px; top: 8px; }

/* Text carried for screen readers only — used where a glyph or a bare number
   would otherwise be the whole message. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Icons are inline SVG at a 2px stroke, sized by the box they sit in and
   coloured by the text beside them. */
.ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 20px;
    height: 20px;
    color: currentColor;
}

.ico svg { width: 100%; height: 100%; display: block; }

/* ============================================================================
   App header
   ----------------------------------------------------------------------------
   Brand and current page on the left, the commands worth reaching from
   anywhere on the right. The header is the only chrome above the workspace:
   destinations live in the rail, so nothing is listed twice.
   ========================================================================== */

.topbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--s-4);
    flex: none;
    min-height: var(--head);
    padding: var(--s-2) var(--s-4);
    background: var(--face);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--e1), var(--lit);
    position: relative;
    z-index: 60;
}

.topbar .brand {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    min-width: 0;
    font-size: var(--fs-md);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--ink);
}

/* The mark: the favicon, at size. Same drawing as assets/favicon.svg, so the
   browser tab and the page agree about what this application is. It is the one
   place the accent is a field of colour rather than an outline. */
.topbar .brand::before {
    content: "";
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: var(--r-sm);
    background:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23fdfbf7' d='M14.6 4.8h2.8a1 1 0 0 1 1 1v1.9c0 2.3 2.9 2.9 2.9 6.1v11.4a2.2 2.2 0 0 1-2.2 2.2h-6.2a2.2 2.2 0 0 1-2.2-2.2V13.8c0-3.2 2.9-3.8 2.9-6.1V5.8a1 1 0 0 1 1-1z'/%3E%3Crect x='10.7' y='17.4' width='10.6' height='4.4' fill='%230d4b47' opacity='0.92'/%3E%3C/svg%3E")
            center / 100% 100% no-repeat,
        linear-gradient(160deg, var(--accent-bright), var(--accent-dark));
    box-shadow: var(--e2), inset 0 1px 0 rgba(255, 255, 255, 0.28);
}

/* The page name rides beside the brand, divided by a hairline. */
.topbar .brand .doc-title {
    padding-left: var(--s-3);
    border-left: 1px solid var(--line-strong);
    font-size: var(--fs-base);
    font-weight: 500;
    color: var(--ink-soft);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The initial standing in for a photograph nobody is going to upload. */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 26px;
    height: 26px;
    margin-left: -4px;
    border-radius: 50%;
    background: var(--accent-wash);
    border: 1px solid #b5d0cb;
    color: var(--accent-ink);
    font-size: var(--fs-micro);
    font-weight: 700;
    line-height: 1;
}

/* ============================================================================
   Command menus
   ----------------------------------------------------------------------------
   Dropdowns in the header: what you can make, and your own account. They are
   commands, not destinations — the rail already answers "where can I go", and
   a second copy of that list is the fastest way to make a small app confusing.
   ========================================================================== */

.menubar {
    display: flex;
    align-items: center;
    gap: var(--s-2);
}

.menu { position: relative; }

.menu-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: var(--tap);
    padding: 0 14px;
    font: inherit;
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink);
    background: var(--face-raised);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: var(--e1), var(--lit);
    cursor: pointer;
    transition: background var(--t-fast) var(--ease),
                box-shadow var(--t-fast) var(--ease);
}

.menu-btn::after {
    content: "";
    width: 7px;
    height: 7px;
    margin-left: 2px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    opacity: 0.55;
}

.menu-btn:hover { background: var(--face); box-shadow: var(--e2), var(--lit); }

/* The primary command — the one thing in the header worth a filled button. */
.menu-btn.is-primary {
    background: var(--accent);
    border-color: var(--accent-dark);
    color: var(--ink-invert);
    box-shadow: var(--e2), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.menu-btn.is-primary:hover { background: var(--accent-bright); }

/* An open menu holds its pressed look, so the button and its list read as one
   object rather than two. */
.menu[data-open="1"] > .menu-btn {
    background: var(--face-pressed);
    box-shadow: var(--inset);
}

.menu[data-open="1"] > .menu-btn.is-primary {
    background: var(--accent-dark);
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.22);
}

.menu[data-open="1"] > .menu-btn::after { transform: translateY(1px) rotate(225deg); }

.menu-list {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 80;
    min-width: 288px;
    margin: 0;
    padding: 6px;
    list-style: none;
    background: var(--face-raised);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: var(--e5);
}

.menu[data-open="1"] .menu-list { display: block; }

.menu-list a {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: var(--tap);
    padding: 0 10px;
    border-radius: var(--r-sm);
    color: var(--ink);
    text-decoration: none;
    font-size: var(--fs-small);
    white-space: nowrap;
}

.menu-list a:hover, .menu-list a:focus {
    background: var(--accent);
    color: var(--ink-invert);
    outline: none;
}

.menu-list a .ico { opacity: 0.75; }
.menu-list a:hover .ico, .menu-list a:focus .ico { opacity: 1; }

/* The shortcut sits hard right, dimmed — it teaches without shouting. */
.menu-list a .accel {
    margin-left: auto;
    padding-left: 20px;
    font-size: var(--fs-micro);
    letter-spacing: 0.04em;
    color: var(--ink-mute);
}

.menu-list a:hover .accel, .menu-list a:focus .accel {
    color: var(--ink-invert);
    opacity: 0.75;
}

.menu-sep {
    height: 1px;
    margin: 5px 8px;
    background: var(--line);
}

/* A caption naming what the group below it is for. */
.menu-cap {
    padding: 8px 10px 4px;
    font-size: var(--fs-micro);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-mute);
}

/* ============================================================================
   Workspace and navigation rail
   ========================================================================== */

.workspace {
    display: flex;
    align-items: stretch;
    gap: var(--gap);
    flex: 1;
    min-height: 0;
    padding: var(--gap);
}

.sidebar {
    flex: none;
    width: var(--rail);
    overflow-y: auto;
    padding: var(--s-3);
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e2), var(--lit);
}

.sidebar-group + .sidebar-group { margin-top: var(--s-4); }

.sidebar-group .sidebar-label {
    display: block;
    padding: 0 10px 6px;
    font-size: var(--fs-micro);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-mute);
}

.sidebar a {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: var(--tap);
    padding: 0 10px;
    margin-bottom: 2px;
    border-radius: var(--r-sm);
    color: var(--ink-soft);
    text-decoration: none;
    font-size: var(--fs-base);
    font-weight: 500;
    transition: background var(--t-fast) var(--ease),
                color var(--t-fast) var(--ease);
}

.sidebar a .lbl { flex: 1; min-width: 0; }

.sidebar a:hover {
    background: var(--face-inset);
    color: var(--ink);
}

/* The current destination is filled, not merely tinted: on a soft surface a
   tint alone is easy to miss, and this is the one thing on screen that has to
   answer "where am I" without being looked for. */
.sidebar a.active {
    background: var(--accent);
    color: var(--ink-invert);
    font-weight: 600;
    box-shadow: var(--e2), inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.sidebar a .ico { opacity: 0.7; }
.sidebar a:hover .ico, .sidebar a.active .ico { opacity: 1; }

/* A count riding on the shortcut: how much work is waiting behind it. */
.sidebar a .tally {
    flex: none;
    min-width: 26px;
    padding: 1px 8px;
    border-radius: var(--r-pill);
    background: var(--accent-wash);
    color: var(--accent-ink);
    font-size: var(--fs-tiny);
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.sidebar a.active .tally {
    background: rgba(255, 255, 255, 0.9);
    color: var(--accent-dark);
}

.content {
    flex: 1;
    min-width: 0;
    overflow-y: auto;
    padding-right: 2px;
}

/* Without the shell (login, print, modal fragments) the content is centred. */
body:not(.has-shell) .content {
    max-width: 1040px;
    margin: 0 auto;
    padding: var(--s-6) var(--gap);
}

/* ============================================================================
   Status bar
   ----------------------------------------------------------------------------
   The one line that is always true: what this screen is for, how much is on
   it, who is signed in, and when.
   ========================================================================== */

.statusbar {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    flex: none;
    padding: 8px var(--s-4);
    background: var(--face);
    border-top: 1px solid var(--line);
    font-size: var(--fs-tiny);
    color: var(--ink-soft);
}

.statusbar .field {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.statusbar .field.grow {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.statusbar .field b { color: var(--ink); font-variant-numeric: tabular-nums; }

/* ============================================================================
   Cards — the unit every screen is built from
   ========================================================================== */

.card {
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e2), var(--lit);
    margin-bottom: var(--gap);
    overflow: hidden;
}

/* The card supplies its own padding through the margins of its children, so
   that the pieces which have to run edge to edge — the title strip, the
   toolbar, a table — can opt out by name instead of being wrapped in markup
   that every page would have to add. */
.card > * {
    margin-left: var(--s-4);
    margin-right: var(--s-4);
}

.card > *:last-child { margin-bottom: var(--s-4); }
.card > *:first-child { margin-top: var(--s-4); }

/* The edge-to-edge list, in one place. */
.card > h1:first-child,
.card > h2:first-child,
.card > .toolbar,
.card > table,
.card > .table-scroll,
.card > .attention,
.card > svg,
.card > .pos-search,
.card > .pos-grid,
.card > .pos-lines,
.card > .pos-empty,
.card > .pos-totals,
.card > .pos-tender,
.card > .pos-actions {
    margin: 0;
}

/* The card title is a strip across the top, so a page of cards reads as a
   stack of named things rather than a wall of text. */
.card > h1:first-child,
.card > h2:first-child {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
    background: var(--face-raised);
    border-bottom: 1px solid var(--line);
    font-size: var(--fs-md);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--ink);
}

/* A right-hand slot in the title strip, for a record count or a status word. */
.card > h1:first-child .title-note,
.card > h2:first-child .title-note {
    margin-left: auto;
    padding: 3px 10px;
    border-radius: var(--r-pill);
    background: var(--face-inset);
    border: 1px solid var(--line);
    font-size: var(--fs-tiny);
    font-weight: 600;
    color: var(--ink-soft);
    white-space: nowrap;
}

/* Headings deeper in a card stay ordinary section labels. */
.card h2, .card h3 {
    margin: var(--s-5) 0 var(--s-2);
    font-size: var(--fs-small);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-mute);
}

.hint {
    font-size: var(--fs-small);
    color: var(--ink-mute);
    line-height: 1.55;
}

/* A one-line trail above a drill-down, so a page reached from somewhere else
   says where "back" is without relying on the browser button. */
.crumb {
    margin: 0 0 var(--s-3);
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-mute);
}

.crumb a { color: var(--accent-ink); text-decoration: none; }
.crumb a:hover { text-decoration: underline; }
.crumb span { margin: 0 6px; opacity: 0.6; }

a { color: var(--accent-ink); }

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--gap);
    align-items: start;
}

/* An explicit column count, for rows of tiles that should not re-wrap ragged. */
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* The fixed count holds only while the tiles are wide enough for the reading
   inside them. On a 1024-wide screen four columns leave about 130px for a
   figure that is now 38px tall, and "P148,210" runs straight out of its tile.
   Below this width the row reflows to whatever fits instead. The rail is still
   standing here — it lies down at 900px, further on. */
@media (max-width: 1180px) {
    .grid-2, .grid-3, .grid-4 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
}

/* ============================================================================
   Toolbar — the strip of controls above a list
   ========================================================================== */

.toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2);
    padding: var(--s-3) var(--s-4);
    background: var(--face);
    border-bottom: 1px solid var(--line);
}

.toolbar form {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    margin: 0;
}

/* The attribute selector is load-bearing: the generic field rule further down
   would otherwise stretch every toolbar control to full width. */
.toolbar label {
    margin: 0;
    font-size: var(--fs-tiny);
    font-weight: 600;
    color: var(--ink-mute);
}

.toolbar input[type=search],
.toolbar input[type=text],
.toolbar select {
    width: auto;
    min-width: 192px;
    margin: 0;
    height: var(--tap);
}

.toolbar .spacer { flex: 1; }

/* A rule between toolbar groups. */
.toolbar .sep {
    width: 1px;
    height: 26px;
    margin: 0 var(--s-1);
    background: var(--line-strong);
}

.toolbar .count {
    font-size: var(--fs-tiny);
    font-weight: 600;
    color: var(--ink-mute);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ============================================================================
   Forms
   ----------------------------------------------------------------------------
   A field is recessed and a button is raised: the surface says which one you
   type into and which one you press. The 1px border is what keeps that true
   when shadows are unavailable.
   ========================================================================== */

label {
    display: block;
    margin: var(--s-4) 0 6px;
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-soft);
}

input[type=text], input[type=password], input[type=number], input[type=date],
input[type=search], input[type=email], input[type=tel], select, textarea {
    display: block;
    width: 100%;
    min-height: var(--tap);
    padding: 8px 12px;
    font: inherit;
    font-size: var(--fs-base);
    color: var(--ink);
    background: var(--field);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    box-shadow: var(--inset);
    transition: border-color var(--t-fast) var(--ease),
                box-shadow var(--t-fast) var(--ease);
}

textarea { min-height: 88px; padding: 10px 12px; resize: vertical; }

select {
    appearance: none;
    padding-right: 34px;
    /* The chevron is drawn rather than downloaded, and inherits nothing that
       could fail to load. */
    background-image:
        linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
        linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
    background-position: calc(100% - 17px) 50%, calc(100% - 12px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    cursor: pointer;
}

input[type=file] {
    display: block;
    width: 100%;
    padding: 9px 12px;
    font: inherit;
    font-size: var(--fs-small);
    background: var(--face-inset);
    border: 1px dashed var(--line-strong);
    border-radius: var(--r-sm);
}

input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--inset), 0 0 0 3px var(--accent-wash);
}

input[readonly], input:disabled, select:disabled, textarea:disabled {
    background: var(--face-inset);
    color: var(--ink-mute);
    box-shadow: none;
    cursor: not-allowed;
}

input[type=checkbox], input[type=radio] {
    width: 20px;
    height: 20px;
    min-height: 0;
    margin: 0 8px 0 0;
    accent-color: var(--accent);
    vertical-align: -3px;
    cursor: pointer;
}

/* Keyboard focus is always visible, and never removed. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-xs);
}

/* An invalid field says so on its edge as well as in its message. */
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
    border-color: var(--red);
    box-shadow: var(--inset), 0 0 0 3px var(--red-wash);
}

/* A field row laid out horizontally — label beside control, not above it. */
.field-row {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin: var(--s-3) 0;
}

.field-row label { margin: 0; flex: none; }
.field-row input, .field-row select { margin: 0; }

/* ============================================================================
   Richer form controls
   ----------------------------------------------------------------------------
   Every one of these replaces a plain text box or a drop-down that was being
   made to do a job it is bad at. Three rules held throughout:

     * The real control is always there. A segmented choice is radio buttons; a
       stepper wraps a number input; a search box filters a <select> that still
       exists. Turn JavaScript off and every form still submits the same fields.
     * The consequence is shown before the commit. A stock adjustment says what
       the balance will become; an audit says the variance as you count. Typing
       a number into a box and hoping is what these screens used to ask for.
     * Nothing is introduced that the keyboard cannot reach, because the shop's
       machine is driven by someone who is faster at typing than at pointing.
   ========================================================================= */

/* --- Segmented choice ------------------------------------------------------
   For two to four options that are the whole choice: increase or decrease,
   bottles or shells, which role. A drop-down hides every option but one behind
   a click, which for a two-way switch is the worst possible trade — it costs a
   click to see a thing that would have fitted on the line. */

.choice {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin: 0 0 var(--s-3);
    padding: 3px;
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: var(--inset);
}

/* The radio itself is the control and keeps every keyboard and screen-reader
   behaviour; it is taken out of the flow rather than hidden, so focus still
   lands somewhere real. */
.choice input[type=radio] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.choice label {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    padding: 7px 12px;
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-soft);
    text-align: center;
    white-space: nowrap;
    border-radius: var(--r-xs);
    cursor: pointer;
    user-select: none;
    transition: background 90ms ease, color 90ms ease;
}

.choice label:hover { background: var(--face-raised); color: var(--ink); }

.choice input[type=radio]:checked + label {
    background: var(--face);
    color: var(--ink);
    box-shadow: var(--e1), var(--lit);
}

/* The ring goes on the label, because the input it belongs to is off-screen. */
.choice input[type=radio]:focus-visible + label {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.choice input[type=radio]:disabled + label {
    opacity: 0.5;
    cursor: not-allowed;
}

/* A choice whose options carry a second line — a price, what it means. */
.choice-rich label { padding: 8px 12px; text-align: left; white-space: normal; }
.choice-rich .choice-note {
    display: block;
    margin-top: 2px;
    font-size: var(--fs-micro);
    font-weight: 400;
    color: var(--ink-mute);
}
.choice input[type=radio]:checked + label .choice-note { color: var(--ink-soft); }

/* Stacked, for options too long to sit side by side. */
.choice-stack { flex-direction: column; }
.choice-stack label { flex: none; text-align: left; }

/* --- Stepper ---------------------------------------------------------------
   A quantity with its own minus and plus. The register has had one since it was
   built; every other quantity in the app was a bare box you had to select and
   retype to change by one. */

.stepper {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin: 0 0 var(--s-3);
    padding: 3px;
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: var(--inset);
}

.stepper button {
    width: 38px;
    height: 38px;
    min-height: 0;
    padding: 0;
    font-size: var(--fs-lg);
    line-height: 1;
    border-radius: var(--r-xs);
}

.stepper input[type=number] {
    width: 86px;
    min-height: 38px;
    margin: 0;
    padding: 2px 4px;
    text-align: center;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: var(--field);
    border-radius: var(--r-xs);
    box-shadow: none;
}

/* The spinners are redundant once there are real buttons, and they steal width
   from the digits. */
.stepper input[type=number]::-webkit-outer-spin-button,
.stepper input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input[type=number] { -moz-appearance: textfield; }

.stepper-sm input[type=number] { width: 64px; min-height: 32px; }
.stepper-sm button { width: 32px; height: 32px; font-size: var(--fs-base); }

/* A unit riding after the number — "cases", "bottles". */
.stepper-unit {
    margin-left: 8px;
    font-size: var(--fs-tiny);
    color: var(--ink-mute);
}

/* --- Searchable list -------------------------------------------------------
   For the product lists, which are thirty-four long and growing. A <select>
   that long is a scroll inside a scroll; this is the same options with a box
   above them. The <select> is still in the markup and still submits. */

.combo { margin: 0 0 var(--s-3); }

.combo-search { margin: 0 0 6px; }

.combo-list {
    max-height: 280px;
    overflow-y: auto;
    background: var(--field);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: var(--inset);
}

.combo-option {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-3);
    width: 100%;
    padding: 8px 10px;
    font-size: var(--fs-small);
    text-align: left;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    box-shadow: none;
    cursor: pointer;
}

.combo-option:last-child { border-bottom: 0; }
.combo-option:hover { background: var(--face-raised); }

.combo-option[aria-selected="true"] {
    background: var(--accent-wash);
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--accent);
}

.combo-option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.combo-option-note {
    flex: none;
    font-size: var(--fs-tiny);
    font-weight: 400;
    color: var(--ink-mute);
    font-variant-numeric: tabular-nums;
}

.combo-empty { padding: 14px 10px; font-size: var(--fs-small); color: var(--ink-mute); text-align: center; }

/* The <select> the list is built from, kept for no-JavaScript and for the form
   submission. JavaScript hides it only once the list is standing. */
.combo[data-ready="1"] .combo-fallback { display: none; }
.combo:not([data-ready="1"]) .combo-search,
.combo:not([data-ready="1"]) .combo-list { display: none; }

/* --- Consequence -----------------------------------------------------------
   What the numbers above will do, worked out as they are typed. This is the
   part these forms were missing: the operator was asked for a figure and told
   the result only after committing, which on an audit screen is exactly
   backwards. */

.consequence {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px var(--s-3);
    margin: 0 0 var(--s-3);
    padding: 10px 12px;
    font-size: var(--fs-small);
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-left: 3px solid var(--line-strong);
    border-radius: var(--r-sm);
}

.consequence b { font-variant-numeric: tabular-nums; }
.consequence .consequence-label { color: var(--ink-mute); }

/* It takes a colour only when it has something to say. */
.consequence.is-good { border-left-color: var(--green); background: var(--green-wash); }
.consequence.is-warn { border-left-color: var(--amber); background: var(--amber-wash); }
.consequence.is-bad  { border-left-color: var(--red);   background: var(--red-wash); }
.consequence.is-idle { opacity: 0.72; }

/* The same readout with its frame taken off, for inside a table cell where the
   row already provides the separation. */
.consequence-inline {
    display: inline-flex;
    gap: 6px;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    font-size: var(--fs-tiny);
}
.consequence-inline.is-good,
.consequence-inline.is-warn,
.consequence-inline.is-bad { background: none; }

/* A variance printed inside a table cell, beside the box that produced it. */
.delta {
    display: inline-block;
    min-width: 4.5em;
    font-size: var(--fs-tiny);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.delta-zero { color: var(--ink-mute); font-weight: 400; }
.delta-up   { color: var(--green-dark); }
.delta-down { color: var(--red-dark); }

/* --- Field group -----------------------------------------------------------
   A labelled block with its hint, so a form reads as a short stack of
   questions rather than a wall of boxes. */

.field { margin: 0 0 var(--s-4); }
.field > label { margin-bottom: 6px; }
.field .hint { margin: 4px 0 0; }
.field > input, .field > select, .field > textarea { margin-bottom: 0; }

/* Two or three fields on a line, collapsing on a narrow screen. */
.field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0 var(--s-4);
}

/* --- Totals bar ------------------------------------------------------------
   The running answer on a form with many lines: what this delivery will cost,
   how many cases are being adjusted. It sticks to the bottom of its card so it
   is still readable with the list scrolled. */

.form-total {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    margin: var(--s-4) calc(-1 * var(--s-4)) calc(-1 * var(--s-4));
    padding: var(--s-3) var(--s-4);
    background: var(--face-inset);
    border-top: 1px solid var(--line);
    box-shadow: 0 -2px 6px rgba(122, 104, 76, 0.07);
}

.form-total-figure {
    font-size: var(--fs-2xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--accent-ink);
}

.form-total-label { font-size: var(--fs-tiny); color: var(--ink-mute); }

/* --- Quiet tag -------------------------------------------------------------
   A small neutral label on a row: "retired", "back to supplier". Deliberately
   not `.status`, which is the coloured semantic badge — these say what a row
   *is*, not how it is doing, and giving them a colour would make a retired
   supplier look like a problem. */

.pill {
    display: inline-block;
    margin-left: 6px;
    padding: 2px 9px;
    font-size: var(--fs-micro);
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: lowercase;
    color: var(--ink-mute);
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    white-space: nowrap;
    vertical-align: middle;
}

/* A row for something retired or inactive: still readable, plainly not current.
   Opacity alone would take the text under contrast; this dims the ink and
   leaves the row's own background to carry it. */
.is-muted td { color: var(--ink-mute); }
.is-muted td b, .is-muted td strong { font-weight: 600; }

/* --- Detail list -----------------------------------------------------------
   Label and value in two columns, for the head of a record screen. A plain <dl>
   puts the value on its own indented line, which for eight short facts is four
   times the height for no gain. */

.detail-grid {
    display: grid;
    grid-template-columns: minmax(11rem, auto) 1fr;
    gap: 2px var(--s-4);
    margin: 0;
}

.detail-grid dt {
    padding: 6px 0;
    font-size: var(--fs-small);
    color: var(--ink-mute);
}

.detail-grid dd {
    margin: 0;
    padding: 6px 0;
    border-bottom: 1px solid var(--line);
}

.detail-grid dt { border-bottom: 1px solid var(--line); }
.detail-grid dt:last-of-type, .detail-grid dd:last-of-type { border-bottom: 0; }

@media (max-width: 560px) {
    .detail-grid { grid-template-columns: 1fr; gap: 0; }
    .detail-grid dt { padding-bottom: 0; border-bottom: 0; }
}

/* The pair of reorder arrows in a table row, kept on one line. The markup has
   carried this class since the categories screen was written; it never had a
   rule, and worked only because two inline-block buttons happen to sit side by
   side. Stating it means a change to button display cannot silently stack them. */
.category-move {
    display: inline-flex;
    gap: 2px;
    margin: 0;
}

/* --- Tables that hold form controls ---------------------------------------
   A table whose cells are filled in as you work has two problems an ordinary
   table does not.

   First, the columns move. With the default `auto` layout the browser sizes
   every column to its widest cell, so the moment a line total appears where
   "Enter a quantity" used to be, every column is re-measured and the whole
   table shifts under the cursor. `fixed` sizes from the first row and the
   colgroup alone, so nothing moves while it is being filled in.

   Second, the controls do not line up with their headings. The generic field
   rule makes every input `display: block`, so `text-align: right` on a numeric
   cell cannot move it — a 7rem box sits flush left under a right-aligned
   heading, which is what made this table look crooked. A control in a numeric
   column fills that column instead, and then its right edge is the column's
   right edge, same as every figure above and below it. */

.table-fixed { table-layout: fixed; }

/* Direct children only: the number box inside a .stepper is sized by the
   stepper and must keep its own width. */
td.num > input[type=number],
td.num > input[type=text] {
    width: 100%;
    margin: 0;
    text-align: right;
}

/* A stepper is inline-flex, so it does follow the cell's text-align; the unit
   that rides after it should stay with it rather than wrapping alone. */
td.num > .stepper { margin: 0; }
td.num > .stepper + .stepper-unit { margin-left: 6px; white-space: nowrap; }

/* A row the operator has actually filled in, so the lines that will do
   something stand out from the forty that will not. */
tr.is-filled { background: var(--accent-wash); }
tr.is-filled td { border-color: var(--line-strong); }

/* ============================================================================
   Buttons
   ----------------------------------------------------------------------------
   Pills, because every action in this app is a short verb and a pill is the
   shape that reads fastest at that length. Pressing one sinks it: the click
   is felt, not merely registered.
   ========================================================================== */

button, .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: var(--tap);
    padding: 0 18px;
    font: inherit;
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
    background: var(--face-raised);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    box-shadow: var(--e1), var(--lit);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--t-fast) var(--ease),
                box-shadow var(--t-fast) var(--ease),
                transform var(--t-fast) var(--ease);
}

button:hover, .btn:hover {
    background: var(--face);
    box-shadow: var(--e2), var(--lit);
}

/* Pressed buttons sink into the surface and shift a pixel. */
button:active, .btn:active {
    background: var(--face-pressed);
    box-shadow: var(--inset-deep);
    transform: translateY(1px);
}

button:disabled, .btn:disabled, button[disabled] {
    background: var(--face-inset);
    color: var(--ink-mute);
    border-color: var(--line);
    box-shadow: none;
    cursor: not-allowed;
    transform: none;
}

/* The commit. One per screen: the thing the operator came to do. */
button[type=submit], .btn-primary {
    background: var(--accent);
    border-color: var(--accent-dark);
    color: var(--ink-invert);
    box-shadow: var(--e2), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

button[type=submit]:hover, .btn-primary:hover {
    background: var(--accent-bright);
    box-shadow: var(--e3), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

button[type=submit]:active, .btn-primary:active {
    background: var(--accent-dark);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.25);
}

.btn-secondary {
    background: var(--face-raised);
    border-color: var(--line-strong);
    color: var(--ink);
}

.btn-danger, button.btn-danger {
    background: var(--face-raised);
    border-color: var(--red);
    color: var(--red-dark);
}

.btn-danger:hover, button.btn-danger:hover {
    background: var(--red-wash);
    color: var(--red-dark);
}

/* A destructive submit is the exception: it is the commit for its form, so it
   is filled the way any other commit would be — just in red. */
button.btn-danger[type=submit] {
    background: var(--red);
    border-color: var(--red-dark);
    color: var(--ink-invert);
}

button.btn-danger[type=submit]:hover { background: var(--red-dark); }

/* Compact button for table rows and toolbars. Still 32px tall, which keeps it
   inside a comfortable pointer target on a dense row. */
.btn-sm, button.btn-sm {
    min-height: 38px;
    padding: 0 15px;
    font-size: var(--fs-small);
}

/* Square, for a button whose whole content is one icon. Every one of these
   carries an aria-label — the picture is not the name. */
.btn-icon, button.btn-icon {
    width: var(--tap);
    padding: 0;
}

button .ico, .btn .ico { width: 18px; height: 18px; }
.btn-icon .ico { width: 20px; height: 20px; }

/* ============================================================================
   Tables
   ----------------------------------------------------------------------------
   The list is what most of these screens are. It is drawn flat inside the card
   rather than raised again: a raised thing inside a raised thing reads as a
   mistake, and the row striping is doing the scanning work anyway.
   ========================================================================== */

table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-base);
}

/* A chart is laid out at 100% and then pushed in by the card padding. */
.card > svg { display: block; padding: var(--s-4); }

/* Wide tables scroll inside their own well rather than pushing the page. */
.table-scroll {
    overflow-x: auto;
    /* The scroll edge needs to read as an edge, or a clipped column looks like
       a missing one. */
    background:
        linear-gradient(to right, var(--face) 30%, rgba(246, 242, 233, 0)) left / 32px 100% no-repeat,
        linear-gradient(to left, var(--face) 30%, rgba(246, 242, 233, 0)) right / 32px 100% no-repeat;
}

th {
    position: sticky;
    top: 0;
    z-index: 2;
    padding: 10px var(--s-4);
    text-align: left;
    background: var(--face-inset);
    border-bottom: 1px solid var(--line-strong);
    font-size: var(--fs-tiny);
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--ink-soft);
    white-space: nowrap;
}

td {
    padding: 11px var(--s-4);
    border-bottom: 1px solid var(--line);
    vertical-align: top;
}

tbody tr:last-child td { border-bottom: 0; }

tbody tr { transition: background var(--t-fast) var(--ease); }
tbody tr:hover { background: var(--face-raised); }

/* Numeric columns line up on the decimal when marked. */
.num { text-align: right; font-variant-numeric: tabular-nums; }
th.num { text-align: right; }
.shrink { width: 1%; white-space: nowrap; }

/* A secondary line inside a cell: the SKU under the product name, the clock
   under the date. */
td .sub {
    display: block;
    margin-top: 2px;
    font-size: var(--fs-tiny);
    color: var(--ink-mute);
}

table input[type=number], table input[type=text], table select {
    width: auto;
    min-width: 112px;
    min-height: 38px;
    margin: 0;
    padding: 5px 9px;
    font-size: var(--fs-small);
}

/* A totals row, so a list of money ends in an answer. */
tfoot td, tfoot th {
    position: static;
    padding: 12px var(--s-4);
    background: var(--face-inset);
    border-top: 1px solid var(--line-strong);
    border-bottom: 0;
    font-weight: 700;
    color: var(--ink);
    text-transform: none;
    letter-spacing: 0;
    font-size: var(--fs-base);
}

/* --- Sortable columns ------------------------------------------------------
   The heading is the control. It carries a permanent dim arrow so it looks
   sortable before it is clicked. */
th[data-sort] {
    cursor: pointer;
    user-select: none;
}

th[data-sort]:hover { background: var(--face-pressed); color: var(--ink); }

th[data-sort]::after {
    content: "\2195";
    margin-left: 6px;
    font-size: var(--fs-micro);
    opacity: 0.4;
}

th[data-sort][aria-sort="ascending"]::after { content: "\2191"; opacity: 1; color: var(--accent-ink); }
th[data-sort][aria-sort="descending"]::after { content: "\2193"; opacity: 1; color: var(--accent-ink); }

/* Rows hidden by the find box. */
/* Rows were guarded individually before the blanket [hidden] rule above; the
   rule is kept because a table row's display is set by the table layout and an
   explicit note here is cheaper than rediscovering why. */
tr[hidden] { display: none; }

/* --- Empty state -----------------------------------------------------------
   "No orders yet." tells the operator nothing. An empty state names the next
   action and offers it. */
.empty {
    padding: var(--s-6) var(--s-4);
    text-align: center;
    color: var(--ink-soft);
}

.empty .empty-glyph {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    margin: 0 auto var(--s-3);
    border-radius: var(--r-md);
    background: var(--face-inset);
    border: 1px solid var(--line);
    box-shadow: var(--inset);
    font-size: var(--fs-2xl);
    line-height: 1;
    color: var(--ink-mute);
}

.empty .empty-glyph .ico { width: 28px; height: 28px; }

.empty b {
    display: block;
    margin-bottom: 4px;
    font-size: var(--fs-md);
    color: var(--ink);
}

.empty .btn { margin-top: var(--s-4); }

/* ============================================================================
   Expanding rows — the orders list
   ========================================================================== */

/* An order opens in its own window rather than pushing the list apart, so each
   row stays where the eye left it. The whole row is the control; the first
   cell carries the affordance that says so. */
.order-row { cursor: pointer; }

.order-row .order-toggle {
    width: 38px;
    padding-left: var(--s-4);
    padding-right: 0;
    color: var(--ink-mute);
    opacity: 0.5;
    transition: opacity var(--t-fast) var(--ease);
}

.order-row:hover .order-toggle,
.order-row:focus-visible .order-toggle { opacity: 1; color: var(--accent-ink); }

.order-row .order-toggle .ico { width: 17px; height: 17px; }

.order-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.order-link { font-weight: 700; text-decoration: none; }
.order-link:hover { text-decoration: underline; }

.order-detail-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-5);
    margin: 0 0 var(--s-4);
    padding: var(--s-3) var(--s-4);
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: var(--inset);
    font-size: var(--fs-small);
}

.order-detail tfoot th { text-align: right; }

/* Agreeing prices before dispatch: the price column becomes editable in place
   rather than opening a second screen, so the operator edits the figure while
   looking at the line it belongs to and the total it feeds. */
.order-reprice .price-input {
    width: 104px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.order-reprice td .hint { display: block; font-size: var(--fs-micro); }

.order-reprice-controls {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-top: var(--s-3);
}

.order-reprice-controls input[type="text"] { flex: 1 1 240px; min-width: 0; }

.order-overrides { margin-top: var(--s-3); font-size: var(--fs-small); }
.order-overrides summary { cursor: pointer; color: var(--ink-soft); }
.order-overrides ul { margin: var(--s-2) 0 0; padding-left: var(--s-4); }
.order-overrides li { margin-bottom: var(--s-1); }
.order-overrides .hint { margin-left: var(--s-1); }

/* --- Grouped orders ---------------------------------------------------------
   The orders list is blocked by the trip carrying each order. The heading is a
   full-width row rather than a repeated column, because the trip is a property
   of the block, not of every line in it — and it gives the whole-trip actions
   somewhere to live that is unambiguously about the trip. */
.order-group > td {
    background: var(--face-inset);
    border-top: 1px solid var(--line-strong);
    border-bottom: 1px solid var(--line);
    padding: var(--s-2) var(--s-3);
}

.orders-table .order-group:first-child > td { border-top: 0; }

.order-group-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-3);
}

.order-group-name {
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
}

.order-group-bar .spacer { flex: 1 1 auto; }

/* --- A trip's receipts, printed as one run ----------------------------------
   Each slip is its own sheet: the customer signs one, the salesman leaves it,
   and two of them sharing a page would have to be cut apart at the door. */
.receipt-slip { max-width: 540px; }

@media print {
    .receipt-page { break-after: page; page-break-after: always; }
    .receipt-page-last { break-after: auto; page-break-after: auto; }
    .receipt-slip { max-width: none; }
}

/* Receipt action and its reference number, on one line under the items. */
.order-detail-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: var(--s-4) 0 0;
}

/* The status control is a toolbar, not a stacked form. */
.order-status-form {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-top: var(--s-4);
}

.order-status-form label { margin: 0; }
.order-status-form select { width: auto; min-width: 192px; margin: 0; }

/* ============================================================================
   Status pills
   ----------------------------------------------------------------------------
   A pill always carries its word. The colour is a second signal, never the
   only one — the same list has to survive being printed in black and white.
   ========================================================================== */

.status {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 11px;
    border-radius: var(--r-pill);
    border: 1px solid transparent;
    font-size: var(--fs-tiny);
    font-weight: 700;
    letter-spacing: 0.02em;
    white-space: nowrap;
    background: var(--face-inset);
    border-color: var(--line-strong);
    color: var(--ink-soft);
}

/* A dot before the word, so the state has a shape as well as a hue. */
.status::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
}

.status-pending, .status-open {
    background: var(--amber-wash);
    border-color: #e0cda1;
    color: var(--amber);
}

.status-out_for_delivery {
    background: var(--accent-wash);
    border-color: #b5d0cb;
    color: var(--accent-ink);
}

.status-delivered, .status-active, .status-closed, .status-balanced {
    background: var(--green-wash);
    border-color: #b8d5c1;
    color: var(--green-dark);
}

.status-cancelled, .status-inactive, .status-discrepancy {
    background: var(--red-wash);
    border-color: #e2bcb8;
    color: var(--red-dark);
}

/* Signed numbers in the ledger: the sign does the work, colour supports it. */
.delta-up { color: var(--green-dark); font-weight: 600; }
.delta-down { color: var(--red-dark); font-weight: 600; }

/* ============================================================================
   Flash messages
   ========================================================================== */

.flash {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 var(--gap);
    padding: 12px var(--s-4);
    border: 1px solid var(--line-strong);
    border-left-width: 4px;
    border-radius: var(--r-md);
    background: var(--face-raised);
    box-shadow: var(--e1);
    font-size: var(--fs-base);
    color: var(--ink);
}

.flash-success { background: var(--green-wash); border-color: #b8d5c1; border-left-color: var(--green); }
.flash-error   { background: var(--red-wash);   border-color: #e2bcb8; border-left-color: var(--red); }
.flash-info    { background: var(--accent-wash); border-color: #b5d0cb; border-left-color: var(--accent); }

/* Inside a card the flash keeps the card padding rather than the page margin. */
/* A flash raised by the form it belongs to keeps the card padding rather than
   the page margin, so it lines up with the fields it is talking about. */
.card > .flash { margin-top: var(--s-4); margin-bottom: 0; }

/* ============================================================================
   Login
   ========================================================================== */

/* --- Landing page ----------------------------------------------------------
   The public front door. Two doors, side by side, because the first question a
   visitor has is which of the two they are — not what the product does. */
.landing {
    max-width: 960px;
    margin: 0 auto;
    padding: var(--s-6) var(--s-4) var(--s-5);
}

.landing-head { text-align: center; margin-bottom: var(--s-6); }

.landing-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 62px;
    height: 62px;
    border-radius: var(--r-lg);
    background: var(--accent);
    color: var(--ink-invert);
    box-shadow: var(--e2), var(--lit);
}

.landing-mark svg { width: 34px; height: 34px; }

.landing h1 {
    margin: var(--s-3) 0 var(--s-1);
    font-size: var(--fs-4xl);
    letter-spacing: -0.4px;
}

.landing-sub { margin: 0; color: var(--ink-soft); font-size: var(--fs-md); }

.landing-doors {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--gap);
    align-items: start;
}

.landing-door {
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e2), var(--lit);
    padding: var(--s-5);
}

.landing-door h2 {
    margin: 0 0 var(--s-2);
    font-size: var(--fs-lg);
}

.landing-door > p {
    margin: 0 0 var(--s-4);
    color: var(--ink-soft);
    font-size: var(--fs-base);
    line-height: 1.5;
}

/* The one action per door is full width: on a phone it is the thumb target,
   and on a desktop it keeps the two doors visually symmetrical. */
.landing-cta {
    display: block;
    width: 100%;
    text-align: center;
    padding: 11px var(--s-4);
    font-size: var(--fs-md);
}

.landing-cta.is-disabled {
    background: var(--face-inset);
    color: var(--ink-mute);
    border-color: var(--line);
    cursor: default;
    box-shadow: none;
}

.landing-meta {
    margin: var(--s-2) 0 0;
    text-align: center;
    font-size: var(--fs-tiny);
    color: var(--ink-mute);
}

/* The QR beside the download: the same app in a browser, for anyone without an
   Android phone to hand. The code sits on its own white tile whatever the
   surface behind it, because a QR on a tinted background is a QR that some
   cameras will not read. */
.landing-qr {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    margin-top: var(--s-4);
    padding: var(--s-3);
    background: var(--face-raised);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: var(--e1), var(--lit);
}

.landing-qr .qr {
    flex: none;
    display: block;
    padding: 4px;
    background: #fff;
    border-radius: var(--r-xs);
}

.landing-qr-say {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    font-size: var(--fs-small);
    color: var(--ink-soft);
}

.landing-qr-say b { font-size: var(--fs-base); color: var(--ink); }
.landing-qr-say a { font-size: var(--fs-tiny); word-break: break-all; }
.landing-qr-note { margin-top: 2px; font-size: var(--fs-tiny); color: var(--ink-mute); }

@media (max-width: 420px) {
    .landing-qr { flex-direction: column; text-align: center; }
    .landing-qr-say { align-items: center; }
}

.landing-help { margin-top: var(--s-4); font-size: var(--fs-small); }

.landing-help summary {
    cursor: pointer;
    color: var(--accent-ink);
    font-weight: 600;
    padding: var(--s-1) 0;
}

.landing-help ol { margin: var(--s-2) 0 0; padding-left: var(--s-5); }
.landing-help li { margin-bottom: var(--s-1); color: var(--ink-soft); line-height: 1.5; }
.landing-help p { margin: var(--s-2) 0 0; color: var(--ink-mute); line-height: 1.5; }

.landing-foot {
    margin-top: var(--s-6);
    text-align: center;
    font-size: var(--fs-tiny);
    color: var(--ink-mute);
}

@media (max-width: 720px) {
    .landing { padding: var(--s-5) var(--s-3) var(--s-4); }
    .landing-doors { grid-template-columns: minmax(0, 1fr); }
    .landing h1 { font-size: var(--fs-3xl); }
}

.login-box {
    max-width: 440px;
    margin: 8vh auto 0;
    padding: var(--s-6);
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-xl);
    box-shadow: var(--e4), var(--lit);
}

.login-box h1 {
    margin: 0 0 var(--s-2);
    font-size: var(--fs-2xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    text-align: center;
    color: var(--ink);
}

.login-box .hint { text-align: center; }
.login-box button[type=submit] { width: 100%; margin-top: var(--s-5); }

/* ============================================================================
   Action bar — the sticky foot of a long form
   ========================================================================== */

.actions-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-3);
    position: sticky;
    bottom: 0;
    z-index: 20;
    margin: var(--gap) 0 0;
    padding: var(--s-3) var(--s-4);
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e4), var(--lit);
}

.actions-bar .total {
    margin-right: auto;
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

/* Keyboard hints, shown next to the action they trigger. */
kbd {
    display: inline-block;
    min-width: 24px;
    padding: 1px 7px;
    background: var(--face-raised);
    border: 1px solid var(--line-strong);
    border-bottom-width: 2px;
    border-radius: var(--r-xs);
    font-family: var(--font);
    font-size: var(--fs-micro);
    font-weight: 700;
    color: var(--ink-soft);
    text-align: center;
}

.keysheet { list-style: none; margin: var(--s-4) 0; padding: 0; }
.keysheet li {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: 8px 0;
    border-bottom: 1px solid var(--line);
    font-size: var(--fs-small);
}
.keysheet li:last-child { border-bottom: 0; }

/* ============================================================================
   Dashboard tiles
   ========================================================================== */

.stat-label {
    display: block;
    font-size: var(--fs-tiny);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-mute);
}

.stat-value {
    display: block;
    margin: var(--s-2) 0 2px;
    font-size: var(--fs-4xl);
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.stat-sub { display: block; font-size: var(--fs-small); color: var(--ink-mute); }

/* A tile is a small card: label, big reading, then the way in. */
.tile {
    display: flex;
    flex-direction: column;
    padding: var(--s-4);
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e2), var(--lit);
}

.tile .tile-link {
    margin-top: var(--s-3);
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--accent-ink);
    text-decoration: none;
}

.tile .tile-link:hover { text-decoration: underline; }

/* A tile earns a coloured edge only when its number actually demands one. */
.tile.is-alert { border-left: 4px solid var(--red); }
.tile.is-alert .stat-value { color: var(--red-dark); }
.tile.is-warn { border-left: 4px solid var(--amber); }
.tile.is-warn .stat-value { color: var(--amber); }
.tile.is-ok { border-left: 4px solid var(--green); }

/* --- Attention list --------------------------------------------------------
   A short list of real records, each a link straight to the work. */
/* --- Chart legend ---------------------------------------------------------
   Identity is never colour alone: every swatch is named, so the chart stays
   readable in greyscale, in print, and to a colourblind reader. */
.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-4);
    margin: var(--s-2) 0 0;
    font-size: var(--fs-tiny);
    color: var(--ink-soft);
}

.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }

.chart-legend i {
    width: 12px;
    height: 12px;
    border-radius: 2px;
    flex: 0 0 auto;
}

/* --- Delivery stops -------------------------------------------------------
   A stop is a card rather than a table row because the thing you most often
   want from a trip is "what is in this order", and a row can only answer that
   by sending you to another page and losing your place in the run. */
.stops { list-style: none; margin: 0; padding: 0; }

.stop {
    border-bottom: 1px solid var(--line);
    padding: var(--s-4);
}

.stop:last-child { border-bottom: 0; }

.stop-head {
    display: flex;
    align-items: center;
    gap: var(--s-3);
}

/* The stop number is the salesman's running order, so it reads as a marker on
   the route rather than as data in a column. */
.stop-seq {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: var(--r-pill);
    background: var(--accent-wash);
    color: var(--accent-ink);
    font-size: var(--fs-small);
    font-weight: 700;
}

.stop-who { flex: 1; min-width: 0; }
.stop-who b { display: block; font-size: var(--fs-base); }
.stop-who .hint { display: block; font-size: var(--fs-small); }

.stop-note {
    margin: var(--s-2) 0 0 38px;
    color: var(--red-dark);
    font-size: var(--fs-small);
}

/* The contents. Inset so the lines read as being inside the stop rather than
   as a second table competing with the one below the card. */
.stop-lines {
    margin: var(--s-3) 0 0 38px;
    width: calc(100% - 38px);
    background: var(--face-inset);
    border-radius: var(--r-sm);
}

.stop-lines td {
    padding: 5px var(--s-3);
    border: 0;
    font-size: var(--fs-small);
}

.stop-lines .qty { white-space: nowrap; font-weight: 600; width: 1%; }

.stop-lines tfoot td {
    border-top: 1px solid var(--line);
    font-size: var(--fs-small);
}

.stop-actions {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin: var(--s-3) 0 0 38px;
    font-size: var(--fs-small);
}

.stop-actions .spacer { flex: 1 1 auto; }

@media (max-width: 640px) {
    /* The indent buys nothing on a narrow screen but width. */
    .stop-lines,
    .stop-actions,
    .stop-note { margin-left: 0; width: 100%; }
}

.attention { list-style: none; margin: 0; padding: 0; }

.attention a {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: 10px var(--s-4);
    border-bottom: 1px solid var(--line);
    color: var(--ink);
    text-decoration: none;
    font-size: var(--fs-base);
}

.attention li:last-child a { border-bottom: 0; }
.attention a:hover { background: var(--face-raised); }
.attention a .ico { color: var(--ink-mute); }
.attention a .who { flex: 1; min-width: 0; font-weight: 600; }
.attention a .when { font-size: var(--fs-tiny); color: var(--ink-mute); white-space: nowrap; }
.attention a .amount {
    min-width: 104px;
    text-align: right;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ============================================================================
   Modals
   ========================================================================== */

.modal-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 100;
    background: rgba(43, 39, 33, 0.42);
    backdrop-filter: blur(2px);
}

.modal-dialog {
    display: none;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 110;
    width: min(560px, calc(100vw - 32px));
    max-height: calc(100vh - 48px);
    background: var(--face);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--e5);
    overflow: hidden;
}

/* An order detail carries a four-column table, which 560px squeezes. */
.modal-dialog.is-wide { width: min(880px, calc(100vw - 32px)); }

.modal-header {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: var(--s-2);
    position: relative;
    z-index: 3;
}

/* A fragment carries no card of its own, so the title belongs to the window. */
.modal-header.has-title {
    padding: var(--s-3) var(--s-3) var(--s-3) var(--s-4);
    background: var(--face-raised);
    border-bottom: 1px solid var(--line);
}

.modal-title {
    flex: 1;
    min-width: 0;
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.modal-close {
    flex: none;
    width: 38px;
    height: 38px;
    min-height: 0;
    padding: 0;
    border-radius: var(--r-sm);
    font-size: var(--fs-xl);
    line-height: 1;
    color: var(--ink-soft);
}

.modal-close:hover { background: var(--red-wash); color: var(--red-dark); border-color: var(--red); }

.modal-content {
    max-height: calc(100vh - 120px);
    overflow-y: auto;
    padding: 0 var(--s-4) var(--s-4);
}

/* A card inside the window loses its own frame — the window is the frame. */
.modal-content .card {
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: none;
    overflow: visible;
}

/* The card inside a window supplies no padding of its own — the window does —
   so every child comes back to the edge. */
.modal-content .card > * { margin-left: 0; margin-right: 0; }
.modal-content .card > *:first-child { margin-top: 0; }
.modal-content .card > *:last-child { margin-bottom: 0; }

.modal-content .card > h1:first-child,
.modal-content .card > h2:first-child {
    margin: 0 calc(var(--s-4) * -1) var(--s-4);
    background: none;
    border-bottom: 1px solid var(--line);
}

/* A photograph sits in a well and is capped so a tall one still fits. */
.modal-photo { margin: 0; }

.modal-photo img {
    display: block;
    width: 100%;
    max-height: 66vh;
    object-fit: contain;
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: var(--inset);
}

.modal-photo-full {
    display: inline-block;
    margin-top: var(--s-3);
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--accent-ink);
}

/* In a window, a back link becomes the cancel action. */
.modal-content a[href*="index.php"] { color: var(--ink-soft); }
.modal-content a[href*="index.php"]:hover { color: var(--ink); }

/* ============================================================================
   Reduced motion
   ----------------------------------------------------------------------------
   Everything here is a courtesy — a hover tint, a button sinking. None of it
   carries meaning, so all of it can go.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    button:active, .btn:active { transform: none; }
}

/* ============================================================================
   Print — drop the chrome, keep the document
   ========================================================================== */

@media print {
    body, body.has-shell {
        background: #fff;
        font-size: 11pt;
        display: block;
        height: auto;
        overflow: visible;
    }
    .no-print, .topbar, .menubar, .sidebar, .statusbar, .toolbar, .actions-bar { display: none !important; }
    .workspace { display: block; padding: 0; }
    .content { overflow: visible; padding: 0; }
    .card {
        border: 1px solid #000;
        border-radius: 0;
        box-shadow: none;
        background: none;
        overflow: visible;
    }
    .card > h1:first-child, .card > h2:first-child {
        background: none;
        color: #000;
        border-bottom: 2px solid #000;
    }
    .card > h1:first-child .title-note, .card > h2:first-child .title-note {
        background: none;
        border: 1px solid #000;
    }
    table { box-shadow: none; }
    th { position: static; background: none; border-bottom: 1px solid #000; color: #000; }
    td { border-bottom: 1px solid #999; }
    .table-scroll { overflow: visible; background: none; }
    .status { border: 1px solid #000; background: none !important; color: #000 !important; }
    .status::before { display: none; }
    th[data-sort]::after { content: ""; }
}

/* ============================================================================
   Narrow screens
   ----------------------------------------------------------------------------
   Below 900px the rail cannot hold its column, so it lies down into a
   scrolling strip above the content — the same links, same order, same
   grouping labels, just rotated.
   ========================================================================== */

@media (max-width: 900px) {
    body.has-shell { height: auto; overflow: visible; }

    .workspace { flex-direction: column; padding: var(--s-2); }

    .sidebar {
        width: auto;
        display: flex;
        gap: var(--s-1);
        align-items: center;
        overflow-x: auto;
        overflow-y: hidden;
        padding: var(--s-2);
        border-radius: var(--r-md);
    }

    .sidebar-group {
        display: flex;
        align-items: center;
        gap: var(--s-1);
    }

    .sidebar-group + .sidebar-group { margin: 0 0 0 var(--s-2); }

    .sidebar-group .sidebar-label {
        padding: 0 var(--s-2) 0 0;
        border-right: 1px solid var(--line);
        white-space: nowrap;
    }

    .sidebar a { margin-bottom: 0; white-space: nowrap; }
    .sidebar a .ico { display: none; }

    .content { overflow: visible; }
    .statusbar { flex-wrap: wrap; }
    .statusbar .field.grow { flex-basis: 100%; }

    .grid-2, .grid-3, .grid-4 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
}

@media (max-width: 640px) {
    :root { --gap: 10px; }

    /* Tiles pair up rather than stacking. Four full-width readings would push
       the list they are summarising off the bottom of a phone screen, which
       inverts what the page is for. */
    .grid-2, .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-2); }
    .tile { padding: var(--s-3); }
    .stat-label { font-size: var(--fs-micro); }
    .topbar { flex-wrap: wrap; padding: var(--s-2); }
    .topbar .brand .doc-title { display: none; }
    .menu-list { right: auto; left: 0; min-width: min(280px, calc(100vw - 24px)); }
    table { font-size: var(--fs-small); }
    th, td { padding-left: 10px; padding-right: 10px; }
    .stat-value { font-size: var(--fs-3xl); }
}

/* ============================================================================
   Walk-in register (POS)
   ----------------------------------------------------------------------------
   Two panes side by side: the catalogue is the input surface, the ticket is
   the readout. The split is fixed rather than stacked because a counter sale
   is a conversation — the operator and the customer both need the running
   total visible while items are still going on.
   ========================================================================== */

.pos {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 410px;
    gap: var(--gap);
    align-items: start;
}

.pos > .card { margin-bottom: 0; }

.pos-ticket {
    position: sticky;
    top: 0;
}

/* --- Search ------------------------------------------------------------- */

.pos-search { padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line); }
.pos-search input { margin: 0; }

/* --- Product grid ------------------------------------------------------- */

.pos-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(172px, 1fr));
    gap: var(--s-3);
    padding: var(--s-4);
}

/* Tiles are buttons, so they are already keyboard reachable and announce as
   controls without any extra wiring. */
.pos-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-height: 96px;
    padding: var(--s-3);
    text-align: left;
    border-radius: var(--r-md);
    background: var(--face-raised);
    border: 1px solid var(--line-strong);
    box-shadow: var(--e2), var(--lit);
}

.pos-tile:hover:not(:disabled) { background: var(--face); box-shadow: var(--e3), var(--lit); }

.pos-tile:active:not(:disabled) {
    background: var(--face-pressed);
    box-shadow: var(--inset-deep);
    transform: translateY(1px);
}

.pos-tile-name {
    font-size: var(--fs-base);
    font-weight: 600;
    line-height: 1.3;
    white-space: normal;
    color: var(--ink);
}

.pos-tile-price {
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--accent-ink);
    font-variant-numeric: tabular-nums;
}

.pos-tile-stock { margin-top: auto; font-size: var(--fs-tiny); font-weight: 500; color: var(--ink-mute); }

/* On the ticket already: the tile carries its own count so the operator can
   see what is on the sale without reading the ticket back. */
.pos-tile.is-active {
    border-color: var(--accent);
    background: var(--accent-wash);
}

.pos-tile.is-active::after {
    content: attr(data-qty);
    position: absolute;
    top: -7px;
    right: -7px;
    min-width: 28px;
    height: 28px;
    padding: 0 6px;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--ink-invert);
    font-size: var(--fs-tiny);
    font-weight: 700;
    line-height: 28px;
    text-align: center;
    box-shadow: var(--e2);
}

.pos-tile.is-out, .pos-tile:disabled {
    background: var(--face-inset);
    border-style: dashed;
    box-shadow: none;
    opacity: 0.75;
}

/* Colour is never the only signal: an out-of-stock tile also says so in words. */
.pos-tile.is-out .pos-tile-stock { color: var(--red-dark); font-weight: 700; }

/* --- Ticket lines ------------------------------------------------------- */

.pos-lines {
    margin: 0;
    padding: var(--s-2) 0;
    max-height: 46vh;
    overflow-y: auto;
}

.pos-empty {
    padding: var(--s-6) var(--s-4);
    text-align: center;
    font-size: var(--fs-small);
    color: var(--ink-mute);
}

.pos-line { padding: var(--s-2) var(--s-4); border-bottom: 1px solid var(--line); }
.pos-line:last-child { border-bottom: 0; }

.pos-line-head {
    display: flex;
    align-items: baseline;
    gap: var(--s-2);
}

.pos-line-name { flex: 1; min-width: 0; font-size: var(--fs-base); font-weight: 600; }

.pos-line-void {
    flex: none;
    width: 30px;
    height: 30px;
    min-height: 0;
    padding: 0;
    border-radius: var(--r-sm);
    font-size: var(--fs-md);
    line-height: 1;
    color: var(--ink-mute);
}

.pos-line-void:hover { background: var(--red-wash); color: var(--red-dark); border-color: var(--red); }

.pos-line-body {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    margin-top: 6px;
}

.pos-line-sub { font-weight: 700; font-variant-numeric: tabular-nums; }
.pos-line-rate { font-size: var(--fs-tiny); color: var(--ink-mute); }

/* What the case is sold as — a swap, or with the bottles, or with the crate
   too. It sits under the quantity rather than beside it because the choice
   changes the rate shown above, and the eye should travel downwards to the
   cause rather than sideways. */
.pos-line-mode { margin-top: 6px; }

.pos-line-mode .pos-mode {
    width: 100%;
    margin: 0;
    padding: 3px 6px;
    font-size: var(--fs-tiny);
}

.pos-stepper {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    background: var(--face-inset);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: var(--inset);
}

.pos-stepper button {
    width: 32px;
    height: 32px;
    min-height: 0;
    padding: 0;
    border-radius: var(--r-xs);
    font-size: var(--fs-md);
    line-height: 1;
}

.pos-stepper .pos-qty {
    width: 50px;
    min-height: 32px;
    margin: 0;
    padding: 2px;
    text-align: center;
    font-weight: 700;
    background: var(--field);
    border-radius: var(--r-xs);
    box-shadow: none;
}

/* --- Totals and tender -------------------------------------------------- */

.pos-totals {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 4px var(--s-3);
    margin: 0;
    padding: var(--s-3) var(--s-4);
    background: var(--face-inset);
    border-top: 1px solid var(--line);
    font-size: var(--fs-base);
}

.pos-totals dt { color: var(--ink-soft); }
.pos-totals dd { margin: 0; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

.pos-totals .grand {
    padding-top: var(--s-2);
    border-top: 1px solid var(--line-strong);
    font-size: var(--fs-xl);
    font-weight: 700;
    color: var(--ink);
}

.pos-tender { padding: var(--s-3) var(--s-4); }
.pos-tender label { margin-top: 0; }

.pos-quick {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: var(--s-2);
}

.pos-quick button { min-height: 38px; padding: 0 14px; font-size: var(--fs-small); }

.pos-change {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-2);
    margin-top: var(--s-3);
    padding: 10px var(--s-3);
    border-radius: var(--r-md);
    background: var(--green-wash);
    border: 1px solid #b8d5c1;
    font-size: var(--fs-small);
    color: var(--green-dark);
}

.pos-change b { font-size: var(--fs-lg); font-weight: 700; font-variant-numeric: tabular-nums; }

/* Short payment is a different state, not a smaller number. */
.pos-change.is-short {
    background: var(--red-wash);
    border-color: #e2bcb8;
    color: var(--red-dark);
}

.pos-actions {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-3) var(--s-4);
    border-top: 1px solid var(--line);
}

.pos-actions #submit-btn { flex: 1; }
.pos-actions .accel { font-size: var(--fs-micro); color: var(--ink-mute); }

/* Below 1040px the ticket cannot hold a useful column beside the grid, so it
   stacks under it. */
@media (max-width: 1100px) {
    .pos { grid-template-columns: minmax(0, 1fr); }
    .pos-ticket { position: static; }
    .pos-lines { max-height: none; }
}

@media (max-width: 640px) {
    .pos-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--s-2); padding: var(--s-2); }
}

/* ---------------------------------------------------------------------------
   The empties account on an order
   --------------------------------------------------------------------------- */

.empties-breakdown { margin-top: var(--s-4); }

.empties-breakdown h3 {
    margin: 0 0 var(--s-2);
    font-size: var(--fs-md);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-mute);
}

/* The rate under each figure is reference, not the number being read. */
.empties-breakdown td small { display: inline-block; color: var(--ink-mute); font-size: var(--fs-tiny); }

/* .is-muted is otherwise a row modifier; the breakdown greys single cells too. */
.empties-breakdown .is-muted { color: var(--ink-mute); }

.empties-total { margin-top: var(--s-3); }
.empties-total td { border: 0; padding: .15rem 0; }
.empties-total .is-total td { border-top: 1px solid var(--line); padding-top: var(--s-2); font-size: var(--fs-lg); }
.empties-rates { margin: var(--s-2) 0 0; font-size: var(--fs-tiny); color: var(--ink-mute); }

/* What a delivery stop's empties adjustment was actually for. */
.stop-empties { margin: var(--s-2) 0 0; font-size: var(--fs-base); }
.stop-empties th { font-size: var(--fs-tiny); letter-spacing: .04em; }
.stop-empties td, .stop-empties th { padding: .3rem .5rem; }
