/* ============================================================================
   DESIGN TOKENS (Batch 100)
   Three layers: PRIMITIVES (raw scales, never used by components) → SEMANTIC
   (intent — the only tokens components reference) → component rules below.

   NOTE: CSS custom properties inherit through the DOM, and Blazor CSS isolation
   only rewrites SELECTORS (adds [b-xxxxx]); it does not sandbox inherited custom
   properties. So this single :root block governs every .razor.css file too.
   Raw hex is permitted ONLY inside the PRIMITIVES block below.
   ============================================================================ */
/* Inter is SELF-HOSTED (UI-1): a till must not depend on fonts.googleapis.com
   at runtime, and the old setup loaded it twice (a render-blocking <link> in
   App.razor AND an @import here that serialised font CSS behind app.css).
   Variable-weight files (wght 100-900) replace the four static weights; the
   family name stays "Inter" so --font-ui and every component are unchanged.
   font-display: swap paints text in the system fallback immediately. */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('fonts/inter-latin-wght-normal.woff2') format('woff2-variations');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('fonts/inter-latin-ext-wght-normal.woff2') format('woff2-variations');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1EFF, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Geist Mono (DS-B) — static weights, self-hosted like Inter. Carries every
   operational numeral via --font-mono; tabular figures are the point. */
@font-face {
    font-family: 'Geist Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/GeistMono-Regular.woff2') format('woff2');
}
@font-face {
    font-family: 'Geist Mono';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('fonts/GeistMono-Medium.woff2') format('woff2');
}
@font-face {
    font-family: 'Geist Mono';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('fonts/GeistMono-SemiBold.woff2') format('woff2');
}
@font-face {
    font-family: 'Geist Mono';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('fonts/GeistMono-Bold.woff2') format('woff2');
}

:root {
    /* ---------- LAYER 1: PRIMITIVES (Paper & Petrol, DS-B) ----------
       Source of truth: design-system/tokens/{colors,spacing,typography}.css.
       Change values THERE first, run `node design-system/scripts/verify.mjs`
       (contrast + repoint-safety gates), then mirror here. */

    /* Paper — the neutral. Warm, thermal-receipt cast. */
    --paper-0:   #ffffff;
    --paper-25:  #fcfbf7;
    --paper-50:  #f6f3ea;
    --paper-100: #ebe6d9;
    --paper-200: #d9d2bf;
    --paper-300: #bfb69e;
    --paper-400: #978c72;
    --paper-500: #6e6550;
    --paper-600: #524b3a;
    --paper-700: #383223;
    --paper-800: #241f15;
    --paper-900: #17130d;
    --paper-950: #0e0b07;

    /* Petrol — the operator brand. Also carries `info`. Not orange/red (those
       mean "late"), not green (means "success"). */
    --petrol-50:  #e8f1f1;
    --petrol-100: #c9dfdf;
    --petrol-200: #97c0c2;
    --petrol-300: #629da1;   /* interactive on the dark skin */
    --petrol-400: #357c82;
    --petrol-500: #1e6069;
    --petrol-600: #14484f;   /* primary action — 9.80:1 on paper-25 */
    --petrol-700: #103a40;
    --petrol-800: #0c2e32;
    --petrol-900: #082124;
    --petrol-950: #041214;

    /* Heat — order age. STATUS ONLY, never brand, never decorative. Each stop
       clears 3:1 on its own canvas and the three ascend strictly. */
    --heat-fresh-light:  #a37d0a;
    --heat-warn-light:   #b05111;
    --heat-urgent-light: #a22416;

    --heat-fresh-dark:   #ae8919;
    --heat-warn-dark:    #e7863c;
    --heat-urgent-dark:  #f2968c;

    --heat-fresh-tint-light:  #fbf2d6;  --heat-fresh-ink-light:  #6b5407;
    --heat-warn-tint-light:   #fbe3cd;  --heat-warn-ink-light:   #7a3a0a;
    --heat-urgent-tint-light: #fadcd9;  --heat-urgent-ink-light: #7a1b12;

    --heat-fresh-tint-dark:  #3a2d06;  --heat-fresh-ink-dark:  #f0dfa8;
    --heat-warn-tint-dark:   #402107;  --heat-warn-ink-dark:   #f7cba4;
    --heat-urgent-tint-dark: #40140f;  --heat-urgent-ink-dark: #f7c4bf;

    /* Dark-skin inks. Never pure white — a kitchen screen is read for eight
       hours straight and pure white glares. */
    --ink-dark-strong:  #e8e4da;
    --ink-dark-default: #cec8ba;

    /* Grass — success ONLY, kept clearly greener than petrol. */
    --grass-500: #0e7a4f;
    --grass-tint-light: #dcf3e8;
    --grass-ink-light: #06452c;
    --grass-tint-dark: #06331f;
    --grass-ink-dark: #a5e3c6;
    --grass-bright: #48c68d;   /* dark skin */

    /* Critical — destructive actions. Distinct from heat-urgent: heat means
       "this order is late", critical means "this button deletes things". */
    --critical-500: #b3261e;
    --critical-tint-light: #fadcd9;
    --critical-ink-light: #7a1b12;
    --critical-tint-dark: #40140f;
    --critical-ink-dark: #f7c4bf;
    --critical-bright: #f2968c;

    /* On-brand foregrounds. MUST stay pure black/white — any softer pair drops
       the worst-case tenant colour below 4.5:1 (design-system/scripts/contrast.mjs). */
    --on-brand-dark:  #000000;
    --on-brand-light: #ffffff;

    /* ---------- LAYER 2: SEMANTIC (components use ONLY these) ---------- */

    /* Brand — THE SWAPPABLE SLOT. Operator apps leave this as petrol; the
       storefront overwrites --brand-* at render time with the tenant's colour. */
    --brand:        var(--petrol-600);
    --brand-hover:  var(--petrol-700);
    --brand-soft:   var(--petrol-50);
    --on-brand:     var(--on-brand-light);
    /* --on-brand is text ON the brand; --brand-ink is the brand AS text. Petrol
       clears 4.5:1 on paper so they collapse here; a tenant's yellow would not. */
    --brand-ink:    var(--brand);
    /* Hover for brand-as-foreground. --brand-hover is a BACKGROUND token; on a
       tenant storefront it carries the raw brand, so it must never reach text. */
    --brand-ink-hover: color-mix(in oklab, var(--brand-ink) 82%, black);

    /* --accent is this file's historical name for the brand slot. */
    --accent:        var(--brand);
    --accent-hover:  var(--brand-hover);

    /* Surfaces */
    --bg-app:      var(--paper-50);
    --bg-sunken:   var(--paper-100);
    --surface-1:   var(--paper-0);
    --surface-2:   var(--paper-25);
    --border-hair: var(--paper-200);

    /* The dim behind an overlay (drawer, modal). ONE value for both skins by
       design — a near-black wash still reads correctly over a dark page — so
       there is no dark override below. Warm, to match the elevation shadows. */
    --scrim:       rgb(23 19 13 / 0.45);

    /* An input's border identifies it as an input, so WCAG 1.4.11 wants 3:1
       against its surface. paper-400 clears it in BOTH skins — no dark override. */
    --border-strong: var(--paper-400);

    /* Typography inks */
    --text-strong:   var(--paper-900);
    --text-default:  var(--paper-700);
    --text-muted:    var(--paper-500);
    --text-faint:    var(--paper-400);
    --text-on-accent: var(--on-brand);

    /* Status — heat. The customer's tracking page and the kitchen docket read
       from these same three tokens. */
    --status-fresh:  var(--heat-fresh-light);
    --status-warn:   var(--heat-warn-light);
    --status-urgent: var(--heat-urgent-light);

    --status-success:  var(--grass-500);
    --status-critical: var(--critical-500);
    --status-info:     var(--petrol-600);   /* petrol does double duty */

    /* Pill surfaces. The skin flips these wholesale. */
    --heat-fresh-tint:  var(--heat-fresh-tint-light);
    --heat-fresh-ink:   var(--heat-fresh-ink-light);
    --heat-warn-tint:   var(--heat-warn-tint-light);
    --heat-warn-ink:    var(--heat-warn-ink-light);
    --heat-urgent-tint: var(--heat-urgent-tint-light);
    --heat-urgent-ink:  var(--heat-urgent-ink-light);
    --grass-tint:       var(--grass-tint-light);
    --grass-ink:        var(--grass-ink-light);
    --critical-tint:    var(--critical-tint-light);
    --critical-ink:     var(--critical-ink-light);

    --focus-ring: color-mix(in srgb, var(--brand) 35%, transparent);

    --skeleton-base:  var(--paper-100);
    --skeleton-sheen: var(--paper-50);

    /* ---------- COMPATIBILITY ALIASES ----------
    Scoped *.razor.css files still reference the old primitive names directly
    (--warning-ink, --success-tint, --slate-0, …). Foreground names alias to
    semantic tokens so they flip with the skin; tints and ramp steps alias to
    primitives. Retire each alias as its call sites move to semantic tokens; do
    not add new ones. */
    --slate-0:   var(--paper-0);
    --slate-25:  var(--paper-25);
    --slate-50:  var(--paper-50);
    --slate-100: var(--paper-100);
    --slate-200: var(--paper-200);
    --slate-300: var(--paper-300);
    --slate-400: var(--paper-400);
    --slate-500: var(--paper-500);
    --slate-600: var(--paper-600);
    --slate-700: var(--paper-700);
    --slate-800: var(--paper-800);
    --slate-900: var(--paper-900);
    --slate-950: var(--paper-950);

    --success-500:    var(--status-success);
    --success-bright: var(--grass-bright);
    --success-tint:   var(--grass-tint);
    --success-ink:    var(--grass-ink);

    --warning-500:    var(--status-warn);
    --warning-bright: var(--heat-warn-dark);
    --warning-tint:   var(--heat-warn-tint);
    --warning-ink:    var(--heat-warn-ink);

    /* Petrol carries `info` — legitimate as a tint or label, NOT as a status
       rail or state indicator. */
    --info-500:  var(--status-info);
    --info-tint: var(--brand-soft);
    --info-ink:  var(--brand-ink);

    --accent-500:    var(--petrol-600);
    --accent-600:    var(--petrol-700);
    --accent-700:    var(--petrol-800);
    --accent-bright: var(--petrol-300);
    --accent-tint:   var(--brand-soft);
    --accent-ink:    var(--brand-ink);

    /* ---------- 8px SPACING SCALE (strict; 4px is the only half-step) ---------- */
    --space-05: 4px; --space-1: 8px;  --space-2: 16px; --space-3: 24px;
    --space-4: 32px; --space-5: 40px; --space-6: 48px; --space-8: 64px; --space-10: 80px;

    /* ---------- RADIUS / ELEVATION / TYPE / MOTION / DENSITY ---------- */
    --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-pill: 999px;

    --elev-1: 0 2px 6px -1px rgb(23 19 13 / 0.05), 0 1px 3px -1px rgb(23 19 13 / 0.03);
    --elev-2: 0 8px 20px -3px rgb(23 19 13 / 0.08), 0 4px 8px -2px rgb(23 19 13 / 0.05);
    --elev-3: 0 24px 48px -12px rgb(23 19 13 / 0.15), 0 12px 24px -6px rgb(23 19 13 / 0.10);

    --font-ui:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
    /* Geist Mono carries every operational numeral (order #, money, timers) —
       tabular figures stop prices jittering as a table re-renders. Self-hosted. */
    --font-mono:"Geist Mono",ui-monospace,"SF Mono","Cascadia Mono","Roboto Mono",Menlo,Consolas,monospace;
    --fs-caption:12px; --fs-body:14px; --fs-base:15px; --fs-h3:18px;
    --fs-h2:22px; --fs-h1:28px; --fs-metric:32px;
    --fs-ticket:40px;   /* the order number on a kitchen docket, read at arm's length */
    --lh-tight:1.15; --lh-body:1.5;
    --weight-regular:400; --weight-medium:500; --weight-semibold:600; --weight-bold:700;
    --tracking-eyebrow:0.08em;

    --motion-fast:120ms; --motion-base:200ms; --ease:cubic-bezier(.2,.6,.2,1);

    /* The heat edge. One width, one place to change it. */
    --heat-edge-width: 4px;

    /* The collapsed dock's height — a layout token because the dock is fixed to
       the viewport bottom and overlaps whatever is behind it; anything that must
       stay clickable reserves this much bottom padding.
       68px, NOT the design system's 80px: the till has shipped a 68px bar since
       Batch B3 and nothing else consumed this token, so 80 was a number the
       product never rendered. The dock-naming convergence made `.dock-bar`'s
       `height: var(--dock-height)` start applying for the first time — adopting
       80 would have grown the always-present bar by 12px on a screen where the
       Checkout button has already been measured below the fold. The token now
       states what the dock actually is. */
    --dock-height: 68px;

    /* Density: `comfortable` is the admin default; `touch` is the till and the
       kitchen. 48px with 8px separation is the minimum finger target on a
       screen someone jabs with a wet thumb during a Friday rush. */
    --control-height: 32px;
    --control-gap: var(--space-1);
    --control-pad-x: var(--space-2);

    /* Explicit light default; html[data-theme="dark"] flips it (UI-5). */
    color-scheme: light;
}

[data-density="comfortable"] {
    --control-height: 32px;
    --control-gap: var(--space-1);
    --control-pad-x: var(--space-2);
}

[data-density="touch"] {
    --control-height: 48px;
    --control-gap: var(--space-1);
    --control-pad-x: var(--space-3);
    --fs-body: 15px;
}

/* Motion is understated by default, and absent when asked. */
@media (prefers-reduced-motion: reduce) {
    :root {
        --motion-fast: 1ms;
        --motion-base: 1ms;
    }
}

/* ============================================================================
   BASE
   ============================================================================ */
html, body {
    margin: 0;
    padding: 0;
    background: var(--bg-app);
    color: var(--text-default);
    font-family: var(--font-ui);
    font-size: var(--fs-base);
    line-height: var(--lh-body);
}

* {
    box-sizing: border-box;
}

h1, h2 {
    border: none !important;
}

/* Routes.razor's <FocusOnNavigate Selector="h1" /> gives every page's <h1>
   tabindex="-1" and calls .focus() after each navigation/refresh so screen
   readers announce the new page — but the element is programmatic-only
   (tabindex=-1 keeps it out of the Tab order), so the browser's default
   focus ring is pure visual noise: the title appears boxed on every page
   switch and refresh (reported as a "red rectangle" around the title). The
   `border: none` above didn't fix it because the browser draws a focus ring
   with `outline`, not `border` — a different property. Suppressing outline
   here is safe because this h1 is never keyboard-tabbable, so no visible
   focus indicator is lost for keyboard users; buttons/inputs/links keep
   their own :focus-visible rings untouched. */
h1:focus {
    outline: none;
}

h1 {
    font-size: var(--fs-h1);
    font-weight: var(--weight-bold);
    color: var(--text-strong);
    letter-spacing: -0.01em;
    margin: 0 0 var(--space-2);
}

a {
    color: var(--accent);
}

/* Form controls do NOT inherit the document font — the UA gives them Arial
   (monospace for textarea), so without this every input, select and textarea in
   the admin renders in a different typeface from the page around it. `.btn`
   carries its own `font: inherit`, which is why BUTTONS looked right and only
   the fields were wrong. Mirrors design-system/tokens/base.css, which the app
   deliberately does not link (it links components.css + this file). */
button,
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

/* Mirrors design-system/tokens/base.css. The UA default is `separate`, which
   draws doubled cell borders on any table that has not opted into `.data-table`. */
table {
    border-collapse: collapse;
}

/* ---- Login / empty layout ---- */
.empty-layout {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}

.login-shell {
    width: 100%;
    max-width: 26rem;
}

.login-card {
    background: var(--surface-1);
    padding: var(--space-4);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-3);
}

/* The service bell — the brand mark on the sign-in card (Service Line).
   Brass disc with dark ink so the glyph stays legible in both themes. */
.login-bell {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    margin-bottom: var(--space-2);
    border-radius: 50%;
    background: var(--accent-bright);
    color: var(--slate-950);
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent-bright) 22%, transparent);
}

.login-card .subtitle {
    color: var(--text-muted);
    margin-top: calc(-1 * var(--space-1));
    margin-bottom: var(--space-3);
}

.login-card .field {
    display: flex;
    flex-direction: column;
    gap: var(--space-05);
    margin-bottom: var(--space-2);
}

/* Deliberately more specific than it looks like it needs to be. The login and
   accept-invite cards wrap their inputs in `.field`, so Batch B's `.field label`
   glue (added with the .form-group → .field rename) ties with a bare
   `.login-card label` on specificity and wins on source order — silently making
   these labels bold slate. The login shell is page chrome, not a form component
   (see the migration plan's "never migrate" list), so it outranks the glue. */
.login-shell .login-card label {
    font-size: var(--fs-caption);
    font-weight: var(--weight-medium);
    color: var(--text-default);
}

.login-card input {
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font: inherit;
}

.login-card input:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 1px;
    border-color: var(--accent);
}

.login-card .submit {
    width: 100%;
    padding: var(--space-1) var(--space-2);
    background: var(--accent);
    color: var(--text-on-accent);
    border: none;
    border-radius: var(--radius-sm);
    font: inherit;
    font-weight: var(--weight-semibold);
    cursor: pointer;
    transition: background var(--motion-fast) var(--ease);
}

.login-card .submit:hover {
    background: var(--accent-hover);
}

/* Google Sign-In button */
.btn-google {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    width: 100%;
    padding: var(--space-1) var(--space-2);
    background: var(--surface-1);
    color: var(--text-default);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font: inherit;
    font-weight: var(--weight-medium);
    text-decoration: none;
    cursor: pointer;
    margin-bottom: var(--space-2);
}

.btn-google:hover {
    background: var(--bg-app);
    border-color: var(--slate-400);
}

/* "or" divider between Google and password form */
.or-divider {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-bottom: var(--space-1);
    color: var(--text-faint);
    font-size: var(--fs-caption);
}

.or-divider::before,
.or-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border-hair);
}

/* Hint for kitchen staff below the divider */
.staff-hint {
    font-size: var(--fs-caption);
    color: var(--text-muted);
    margin-bottom: var(--space-2);
    text-align: center;
}

.alert {
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-2);
    font-size: var(--fs-body);
}

.alert-warning {
    background: var(--warning-tint);
    color: var(--warning-ink);
    border: 1px solid color-mix(in srgb, var(--warning-500) 35%, transparent);
}

.alert-error {
    background: var(--critical-tint);
    color: var(--critical-ink);
    border: 1px solid color-mix(in srgb, var(--critical-500) 35%, transparent);
}

.validation-message {
    color: var(--critical-ink);
    font-size: var(--fs-caption);
}

/* ---- Shared admin helpers (used by create / edit pages) ---- */
/* .breadcrumb moved to the design system's Navigation family. */

.muted {
    color: var(--text-muted);
}

/* Visually-hidden but screen-reader-available (e.g. a <legend> whose visible
   label is already provided by an accordion header). */
.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;
}

/* Inline field-level validation message shown under a form input. */
.field-error {
    margin: var(--space-05) 0 0;
    font-size: var(--fs-body);
    color: var(--critical-ink);
}

/* The "nudge": a recommended-but-never-required field (e.g. phone on an
   unpaid timed collection). Amber, encouraging — deliberately NOT the error
   red, because placement is still allowed. */
.field-hint {
    margin: var(--space-05) 0 0;
    font-size: var(--fs-body);
    color: var(--warning-ink);
}

.field-nudge .form-control {
    border-color: var(--warning-ink);
}

/* ---- Global Admin Design System ---- */
/* "Back to the Settings hub" link at the top of every Settings sub-page — the
   sub-pages are reached from /client/settings and otherwise leave the user with
   no way back except the sidebar nav. */
.settings-back {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: var(--space-2);
    font-size: var(--fs-body-sm, 0.85rem);
    font-weight: var(--weight-medium, 500);
    color: var(--text-muted);
    text-decoration: none;
}

.settings-back:hover {
    color: var(--text-strong);
    text-decoration: underline;
}

.settings-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
    flex-wrap: wrap;
}

.settings-header h1 {
    margin: 0;
}

.settings-header .btn {
    margin-left: auto;
}

.color-picker {
    padding: 2px !important;
    height: 40px !important;
    width: 100px !important;
    max-width: 100px !important;
    cursor: pointer !important;
}

/* ---- Dash Grid ----
   The tile itself is the design system's `.card.stat-tile`, and .stat-label /
   .stat-value / .stat-sub now come from components.css (Batch A). What stays
   here is the grid that lays the tiles out and the page-local value variants:
   a colour, or a prose value, applied on top of the shared metric type. */
.dash-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 1rem;
}

.stat-value small {
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--text-muted);
    margin-left: 0.2rem;
}

/* Not every tile holds a metric. `.text` opts a prose value ("Accepting
   orders", "1 min ago") out of the mono figure face — the design system sets
   .stat-value in --font-mono at --fs-metric because it is a number someone
   compares, which is exactly wrong for a sentence. */
.stat-value.text {
    font-family: var(--font-ui);
    font-size: var(--fs-h2);
    font-weight: var(--weight-semibold);
}

.stat-value.ok { color: var(--success-500); }
.stat-value.warn { color: var(--warning-ink); }
.stat-value.muted { color: var(--text-faint); font-weight: 500; }

.settings-panel {
    background: var(--surface-1);
    padding: var(--space-4);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-hair);
    box-shadow: var(--elev-1);
    max-width: 800px;
    margin-bottom: var(--space-4);
    position: relative;
    overflow: hidden;
}

.settings-panel h2 {
    margin-top: 0;
    margin-bottom: var(--space-3);
    font-size: var(--fs-h3);
    color: var(--text-strong);
}

.settings-panel p.muted {
    margin-top: 0;
    margin-bottom: var(--space-3);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
}

/* ---- Form fields ----
   `.field` and `.input` are the design system's (Batch B); what stays here is
   the glue it deliberately omits, because the specimen composes a bare form
   while ours carries a page rhythm:
     - `.field` has no bottom margin of its own — without this the stacked
       fields on every settings page collapse into one block.
     - the design system labels via a `.field-label` CLASS; ours are bare
       <label> elements inside the field, so the element selector stays.
     - `.input` sets no width. Inside `.field` (flex column, align-items
       stretch) it fills anyway, but ~25 inputs sit outside one. */
.field { margin-bottom: var(--space-3); }

/* `--text-default`, not `--slate-600`. The slate compat alias resolves to
   `--paper-600` — a LIGHT-mode ink — which never flips, so every form label in
   the admin rendered #524b3a on the dark canvas: 1.89:1, across nine pages.
   This matches the design system's own `.field-label`; these labels are bare
   <label> elements, so they never picked that class up. */
.field label {
    font-size: var(--fs-caption);
    font-weight: var(--weight-bold);
    color: var(--text-default);
}

/* The qualifier half of a label — "Shop — only if you signed in with a user ID".
   A bare <span> inside the label inherits the rule above, so the caveat came out
   at the same weight as the field name and read as part of it. */
.field-optional {
    font-weight: var(--weight-regular);
    color: var(--text-muted);
}

.field .input,
.settings-panel .input { width: 100%; }

/* A checkbox reads left-to-right — box, then label — where `.field` stacks a
   label ABOVE its control. Every page that wanted the row wrote
   `class="field checkbox-field"` and nothing defined the second half, so the
   box sat on its own line with the label orphaned beneath it. `.field`'s
   caption-sized bold label is also wrong here: this one is the control's name,
   not a field heading, so it takes body type. */
.checkbox-field {
    flex-direction: row;
    align-items: center;
    gap: var(--space-1);
}
.checkbox-field label {
    font-size: var(--fs-body);
    font-weight: var(--weight-regular);
}

/* "Custom colour" checkbox that gates the colour picker on the item + category
   forms — a native <input type="color"> has no empty state, so this is the
   "inherit the category colour" switch. Row layout: checkbox beside its label. */
.colour-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    cursor: pointer;
    user-select: none;
}
.colour-toggle input[type="checkbox"] {
    width: auto;
    margin: 0;
}

/* ---- .form-control — POS ONLY, pending retirement ----
   The admin pages use the design system's `.input`. These rules survive for
   `Pos.razor` and `PosBottomDock.razor`, which sit inside
   `data-density="touch"` where `.input`'s `min-height: var(--control-height)`
   resolves to 48px — the sizing change the migration plan sequences last, on
   the surface where a regression costs a shop money on a Friday night. Migrate
   these two files and delete this block together, in Batch E. */
.form-control {
    padding: var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font: inherit;
    width: 100%;
    background: var(--surface-1);
    box-shadow: inset 0 2px 4px rgba(23, 27, 38, 0.02);
    transition: border-color var(--motion-fast) var(--ease),
                box-shadow var(--motion-fast) var(--ease);
}

.form-control:hover {
    border-color: var(--slate-400);
}

.form-control:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus-ring), inset 0 2px 4px rgba(23, 27, 38, 0.02);
}

.checkbox-row {
    display: flex;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

.checkbox-row label {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    font-weight: var(--weight-medium);
}

.form-actions,
.settings-actions {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
}

/* ============================================================================
   BUTTON SKIN — the design system owns the BOX, this file owns the decoration.

   Until UI-7 both files declared the whole family and this one won by loading
   second, so the shipped button was a hybrid nobody designed: `min-height` from
   the design system (the only property it alone declared), everything else from
   here. Which file won each property was an accident of what each happened to
   list. The box now lives in ONE place — the design system's Actions family,
   whose version is token-driven and density-aware (`min-height:
   var(--control-height)`, so targets grow under `data-density="touch"`).

   What stays here is the admin's skin and nothing else: the coloured drop
   shadows and the hover lift. They cannot move into the component layer —
   `audit-adherence.mjs` forbids raw px outside 1/2/4/6 and these need 10px and
   -2px offsets — so this is a deliberate two-layer split, not a duplicate.
   Add a SHAPE change to the design system; add a decoration here.
   ============================================================================ */
.btn:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 2px;
}

/* Compact button for dense rows — the menu builder's size/tier editors, the
   staff table's row actions. 35 buttons across five pages had been asking for
   it as `btn-small` or `btn-sm`, neither of which existed, so every one of them
   rendered at full height in a row laid out for a short one. Back-office only:
   the POS keeps `--control-height` so its targets still grow with
   `data-density="touch"`. */
.btn-sm {
    padding: var(--space-05) var(--space-1);
    font-size: var(--fs-caption);
}

/* `--accent` is an alias of the design system's `--brand` (verified identical in
   both skins), so only the shadow is added here. */
.btn-primary {
    box-shadow: 0 4px 10px -2px color-mix(in srgb, var(--accent) 50%, transparent);
}

.btn-primary:hover {
    background: var(--accent-hover);
    box-shadow: 0 6px 14px -3px color-mix(in srgb, var(--accent) 60%, transparent);
    transform: translateY(-1px);
}

.btn-secondary {
    box-shadow: var(--elev-1);
}

.btn-secondary:hover {
    background: var(--slate-50);
    box-shadow: var(--elev-2);
    transform: translateY(-1px);
}

/* Fixed white, NOT `--text-on-accent`. That token flips to a dark ink for dark
   mode — right for the brand button, whose background lightens to petrol-300,
   and wrong here: `--critical-500` is the same #b3261e in both themes, so the
   flip put black on red at 3.21:1 ("Delete", "Pause online ordering"). Matches
   the design system's own `.btn-danger`, which already uses `--on-brand-light`. */
.btn-danger {
    box-shadow: 0 4px 10px -2px color-mix(in srgb, var(--critical-500) 50%, transparent);
}

.btn-danger:hover {
    background: var(--critical-ink);
    box-shadow: 0 6px 14px -3px color-mix(in srgb, var(--critical-500) 60%, transparent);
    transform: translateY(-1px);
}

/* ============================================================================
   SHARED LIST-PAGE SYSTEM (filters / tables / pagination)
   ============================================================================ */
/* .filter-bar / .filter-field / .filter-submit / .filter-reset moved to the
   design system's Forms family (Batch D). The scoped `min-width` widenings in
   Tenants.razor.css and WebhookLogs.razor.css are ADDITIVE and still apply. */

/* ---- Page header ----
   The heading ROW: a page title with its primary action on the right. It lived
   in Tenants.razor.css, where CSS isolation kept it to that one page while
   three others wrote `class="page-header"` and silently got nothing. Those
   three were title BLOCKS — a heading above a paragraph of description — which
   this rule would have laid out side by side, so they were renamed rather than
   adopted. Use it only where there is an action to put on the right. */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

/* ---- Role pills ----
   A staff role, tinted by role. Lives here rather than in the design system
   because `owner` / `manager` / `kitchenstaff` are this product's roles, not a
   general vocabulary — and here rather than in Staff.razor.css because BOTH
   staff tables render it: the tenant-side list and the platform's
   TenantStaff. It was scoped to the first, so the second — which copies the
   markup verbatim — rendered its roles as bare text. */
.role-pill {
    display: inline-block;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    background: var(--border-hair);
    color: var(--text-default);
}

/* Pair the tint with its OWN ink. `--accent-700` is a raw ramp step, so it did
   not flip for dark mode the way `--info-tint` did: the pill rendered petrol-800
   on petrol-900 — 1.16:1, invisible — while the Manager and Kitchen Staff pills
   beside it read fine, because they already use matched semantic pairs. */
.role-pill.role-owner {
    background: var(--info-tint);
    color: var(--info-ink);
}

.role-pill.role-manager {
    background: var(--accent-tint);
    color: var(--accent-ink);
}

.role-pill.role-kitchenstaff {
    background: var(--warning-tint);
    color: var(--warning-ink);
}

/* ---- Empty states ----
   Used by Invoices, Scheduled orders, Driver analysis, Orders and the platform
   Subscriptions page, all of which referenced `.empty-state` while nothing
   defined it — so every one of them rendered its "nothing here yet" as bare
   text on the page background. It is deliberately a `.card`-shaped surface so an
   empty list reads as the same object the populated list will be. */
.empty-state {
    background: var(--surface-1);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-1);
    padding: var(--space-4);
    color: var(--text-muted);
    text-align: center;
}

.empty-state p { margin: 0; }

/* Invoices' pager row (was a set of Bootstrap utility classes that this app
   does not ship). */
.invoice-paging {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: var(--space-2);
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface-1);
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1px solid var(--border-hair);
    box-shadow: var(--elev-1);
}

.data-table th,
.data-table td {
    text-align: left;
    padding: var(--space-1) var(--space-2);
    border-bottom: 1px solid var(--border-hair);
    font-size: var(--fs-body);
}

/* Operational numerals — order #, money, timers, counts — read as till-roll
   figures (POS "Service Line"). Opt-in via class="num"; tabular by default. */
.num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

/* Right-align + tabular figures for numeric columns (add class="num" in markup) */
.data-table td.num,
.data-table th.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.data-table thead th {
    position: sticky;
    top: 0;
    background: var(--bg-sunken);
    font-size: var(--fs-caption);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.data-table tbody tr {
    transition: background var(--motion-fast) var(--ease);
}

.data-table tbody tr:hover {
    background: var(--slate-50);
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.data-table code {
    background: var(--bg-sunken);
    padding: 2px var(--space-05);
    border-radius: var(--radius-sm);
    font-size: var(--fs-caption);
}

/* .pagination / .page-indicator moved to the design system's Navigation
   family (Batch D). */

/* ---- Provider pill ----
   Status pills are the design system's `.pill` + `.pill-{success,warn,critical,
   neutral}` (Batch A): same colours (--success-tint IS --grass-tint), plus the
   glyph that keeps a status readable in greyscale. `.provider-pill` stays here
   — it labels a payment provider, which is a category, not a status. */
.provider-pill {
    display: inline-block;
    padding: var(--space-05) var(--space-1);
    border-radius: var(--radius-pill);
    font-size: var(--fs-caption);
    font-weight: var(--weight-semibold);
    background: var(--accent-tint);
    color: var(--accent-ink);
}

/* ============================================================================
   DARK SKIN — the semantic tokens, re-pointed
   One token set serves the whole app. The NavMenu theme toggle (circuit-free
   tkThemeToggle, persisted to localStorage 'tk-theme', defaulting to
   prefers-color-scheme) stamps [data-theme="dark"] on <html>, so every page
   themes through these tokens with zero per-component work — including the POS,
   whose tiles tint --cat-accent over --surface-1 and so keep both their
   colour-coding and their legibility with no per-colour handling. It is also
   why the layout padding sits on the dark --bg-app rather than leaving a thin
   light edge outside the POS work area.

   Two per-surface skins are RETIRED and must not come back: the POS-local
   [data-theme="pos-dark"] (it duplicated the app-wide toggle) and
   [data-theme="kds"] (see below). One token set serves every surface.
   ============================================================================ */
html[data-theme="dark"],
[data-theme="dark"] {
    /* Native form controls (selects, date inputs, scrollbars) render dark. */
    color-scheme: dark;

    /* Canvas is a warm near-black, keeping the paper lineage. Text is never
       pure white, and the heat stops desaturate — saturated colour on a dark
       canvas vibrates, the last thing a kitchen screen needs at 11pm. */
    --bg-app:      var(--paper-900);
    --bg-sunken:   var(--paper-950);
    --surface-1:   var(--paper-800);
    --surface-2:   var(--paper-700);
    --border-hair: var(--paper-700);
    /* --border-strong deliberately NOT overridden: paper-400 clears 3:1 on
       both canvases. */

    --text-strong:  var(--ink-dark-strong);
    --text-default: var(--ink-dark-default);
    --text-muted:   var(--paper-300);
    --text-faint:   var(--paper-400);

    /* Petrol-600 is unreadable on a dark canvas; step up the ramp. */
    --brand:       var(--petrol-300);
    --brand-hover: var(--petrol-200);
    --brand-soft:  var(--petrol-900);
    --on-brand:    var(--on-brand-dark);
    --brand-ink:   var(--brand);
    /* Toward white here: on a near-black canvas, hover must brighten. */
    --brand-ink-hover: color-mix(in oklab, var(--brand-ink) 82%, white);

    --accent:       var(--brand);
    --accent-hover: var(--brand-hover);

    --status-fresh:  var(--heat-fresh-dark);
    --status-warn:   var(--heat-warn-dark);
    --status-urgent: var(--heat-urgent-dark);

    --status-success:  var(--grass-bright);
    --status-critical: var(--critical-bright);
    --status-info:     var(--petrol-300);

    --heat-fresh-tint:  var(--heat-fresh-tint-dark);
    --heat-fresh-ink:   var(--heat-fresh-ink-dark);
    --heat-warn-tint:   var(--heat-warn-tint-dark);
    --heat-warn-ink:    var(--heat-warn-ink-dark);
    --heat-urgent-tint: var(--heat-urgent-tint-dark);
    --heat-urgent-ink:  var(--heat-urgent-ink-dark);
    --grass-tint:       var(--grass-tint-dark);
    --grass-ink:        var(--grass-ink-dark);
    --critical-tint:    var(--critical-tint-dark);
    --critical-ink:     var(--critical-ink-dark);

    --skeleton-base:  var(--paper-700);
    --skeleton-sheen: var(--paper-600);

    /* Harder, flatter shadows read better on a self-lit screen (kept from the
       brass skin; the design system does not flip elevation). */
    --elev-1: 0 1px 0 rgba(0,0,0,.55);
    --elev-2: 0 2px 0 rgba(0,0,0,.55);
}

/* The KDS shell is GONE. `.kds-shell` and the `[data-theme="kds"]`
   stamp had ZERO uses in markup or code — the kitchen-display pages retired when
   the POS dock became the one operational surface, and the skin outlived them.
   The dark token re-points above are shared with the app-wide dark theme and
   stay; only the `kds` selector was removed from that list. If a wall display is
   ever built (the design system still carries a PROPOSED page for one), it gets
   the dock family, not a resurrected private skin. */

/* ---- Thermal receipt print rules — unchanged, functional ---- */
@media print {
    @page {
        size: 80mm auto;
        margin: 4mm 3mm;
    }

    /* Hide all layout siblings so they don't take up space and spawn blank pages.
       Two receipts exist on POS (walk-in #thermal-receipt + the doorbell's
       kitchen-feed one), both tagged .print-receipt; only one is populated at a
       time, so the empty one contributes nothing to the print. (Batch B5b-2.) */
    body *:not(:has(.print-receipt)):not(.print-receipt):not(.print-receipt *) {
        display: none !important;
    }

    /* Strip layout properties from ancestors so the receipt sits exactly at 0,0 */
    body *:has(.print-receipt) {
        margin: 0 !important;
        padding: 0 !important;
        border: none !important;
        min-height: 0 !important;
        height: auto !important;
        position: static !important;
        display: block !important;
    }

    .print-receipt {
        display: block !important;
        width: 74mm;
        margin: 0;
        padding: 0;
    }
}

/* ---- SignalR reconnection UI (App.razor #components-reconnect-modal) ----
   blazor.web.js toggles components-reconnect-{show,hide,failed,rejected} on
   the modal. Hidden by default; shown as a centred overlay while a dropped
   circuit is being recovered.
   Top of the stack (toasts 300, doorbell 1000, confirm 1050): a dropped circuit
   makes every dialog beneath it dead, so it must always be able to surface. Give
   it a strictly higher z-index — a tie resolves on DOM order, which is luck. */
#components-reconnect-modal {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.5);
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: flex;
}

#components-reconnect-modal .reconnect-dialog {
    background: #fff;
    color: #1a1a1a;
    padding: 1.25rem 1.5rem;
    border-radius: 8px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
    max-width: 22rem;
    text-align: center;
}

#components-reconnect-modal .reconnect-dialog button {
    margin-left: 0.5rem;
    padding: 0.35rem 0.75rem;
    border: 0;
    border-radius: 6px;
    background: #2563eb;
    color: #fff;
    cursor: pointer;
}

/* Show only the message matching the current state. */
#components-reconnect-modal .reconnect-failed,
#components-reconnect-modal .reconnect-rejected {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-loading,
#components-reconnect-modal.components-reconnect-failed .reconnect-rejected {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-failed {
    display: block;
}

#components-reconnect-modal.components-reconnect-rejected .reconnect-loading,
#components-reconnect-modal.components-reconnect-rejected .reconnect-failed {
    display: none;
}

#components-reconnect-modal.components-reconnect-rejected .reconnect-rejected {
    display: block;
}

/* MainLayout ErrorBoundary fallback. */
.error-boundary {
    margin: 1.5rem;
}

/* ============================================================================
POS DRAWERS — the frame lives in the DESIGN SYSTEM
.drawer-backdrop / .line-drawer / .drawer-head(-titles) / .drawer-close /
.drawer-body / .drawer-section (+ its @keyframes) / .drawer-half-chip /
.drawer-foot / .drawer-total / .drawer-step-count are the Overlay family in
design-system/components.css, which is linked BEFORE this file. Do NOT
redeclare them here — a scoped copy wins on order and silently takes the
family back off the design system.

What stays below is NOT the drawer: the size/modifier pickers are their own
families, and .invalid-pill / .instructions-input are page chrome the
migration plan lists as never-migrate.
============================================================================ */

.invalid-pill {
    background: var(--warning-tint);
    color: var(--warning-ink);
    font-size: 0.7rem;
    font-weight: 600;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
}

.size-options {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.size-option {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.75rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.4rem;
    cursor: pointer;
    /* --surface-1, NOT the --slate-0 compat alias: slate-0 is not re-pointed on
       the dark skin, so it stays #ffffff and these chips rendered white under
       light ink inside an otherwise-correct drawer. Same bug the drawer shell
       itself had until Batch C. (Batch E1.) */
    background: var(--surface-1);
    font-size: 0.9rem;
}

.size-option.active {
    background: var(--accent-tint);
    border-color: var(--accent);
}

.size-option input[type="radio"] {
    margin: 0;
}

.size-name { font-weight: 500; }
.size-price { color: var(--text-default); }

.mod-options {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.mod-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border-hair);
    border-radius: 0.4rem;
    cursor: pointer;
    /* --surface-1, NOT the --slate-0 compat alias: slate-0 is not re-pointed on
       the dark skin, so it stays #ffffff and these chips rendered white under
       light ink inside an otherwise-correct drawer. Same bug the drawer shell
       itself had until Batch C. (Batch E1.) */
    background: var(--surface-1);
    font-size: 0.9rem;
}

.mod-option.active {
    background: var(--accent-tint);
    border-color: var(--accent);
}

.mod-name { flex: 1 1 auto; }
.mod-delta { color: var(--text-default); font-weight: 500; }

.instructions-input {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid var(--border-strong);
    border-radius: 0.4rem;
    font: inherit;
    resize: vertical;
    box-sizing: border-box;
}

/* Shared POS payment styles — phone-order form (Pos.razor) + checkout modal
   (PosCheckoutModal). Global so both components' elements match (Batch 157). */
.payment-method {
    border: 1px solid var(--border-hair);
    border-radius: 0.4rem;
    padding: 0.4rem 0.7rem 0.5rem;
    margin: 0 0 0.75rem;
}

.payment-method legend {
    font-size: 0.8rem;
    color: var(--text-muted);
    padding: 0 0.3rem;
}

.payment-method label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0;
    font-size: 0.95rem;
}

.place-order,
.place-phone-order {
    width: 100%;
    min-height: 56px;
    padding: var(--space-2);
    font-size: var(--fs-h3);
}

/* ============================================================================
   SKELETON LOADING (UI-4)
   Layout-true placeholders shown while a page's FIRST data load is in flight,
   so a card grid doesn't visibly "pop" in on the prerender→data transition.
   Mirror the real component's shape (.card.stat-tile etc.) and swap only the text
   spans for .skeleton bars. The shimmer is decorative — static under
   prefers-reduced-motion. Colors come from the semantic skeleton tokens.
   ============================================================================ */
/* The .skeleton shimmer — plus its @keyframes and its
   prefers-reduced-motion rule — moved to the design system's Feedback
   family (Batch D). Only the SIZES stay here: a skeleton must be layout-true
   for the thing it replaces, so its dimensions belong to the consumer, and
   these two are measured against our stat tile. (They are also 12px/28px,
   which the component layer's spacing lint would reject.) */
/* Bar sizes matched to the stat-tile type scale. */
.skeleton-label { width: 45%; height: 12px; }
.skeleton-value { width: 70%; height: 28px; }

/* ============================================================================
   INLINE-SVG ICONS (UI-3 — Components/Shared/Icon.razor)
   1em sizing means an icon inherits the font-size of the slot its emoji
   predecessor occupied; -0.125em nudge sits the 24×24 viewBox on the text
   baseline the way glyphs did.
   ============================================================================ */
.icon {
    display: inline-block;
    vertical-align: -0.125em;
    flex-shrink: 0;
}

/* ---- Theme toggle (UI-5) — circuit-free icon swap ---------------------- */
/* The NavMenu toggle renders BOTH icons; the html-level theme attribute picks
   which is visible, so plain-JS tkThemeToggle needs no component re-render. */
.theme-toggle .theme-icon-sun { display: none; }
html[data-theme="dark"] .theme-toggle .theme-icon-sun { display: inline-block; }
html[data-theme="dark"] .theme-toggle .theme-icon-moon { display: none; }
