/* ============================================================================
   BunHouse Design System
   "Corporate Modern with Tactile Crispness" — specialty roastery / cafe ops.
   ----------------------------------------------------------------------------
   Plain CSS. No build step. No @import. No CDN. Works from file:// in Electron.

   HOW TO USE THIS FILE
   --------------------
   1.  Load fonts.css FIRST, then this file:
         <link rel="stylesheet" href="shared/fonts.css">
         <link rel="stylesheet" href="shared/design-system.css">
   2.  HARD RULE for this app: never write an inline `style="..."` attribute and
       never build a look out of a pile of single-purpose utility classes.
       Every visual element gets a semantic class from the catalogue below.
       If something you need is missing, ADD A CLASS HERE — do not inline it.
   3.  design-system-preview.html renders every class in this file with its name
       next to it. Check that page before building a screen.

   TABLE OF CONTENTS
   -----------------
     00  Reset & base
     01  Design tokens (:root)
     02  Typography classes
     03  Layout primitives  .stack .cluster .divider .scroll-area
     04  Responsive grid    .layout-grid .col-span-* .grid-2/3/4 .kpi-grid
     05  App shell          .app-shell .app-sidebar .nav-item .app-topbar .app-main
     06  Page header        .page-header .page-title .page-subtitle .breadcrumb
     07  Buttons            .btn + variants
     08  Cards              .card .metric-card
     09  Chips & badges     .chip .badge-count .tag-dot .kbd .avatar
     10  Tables             .table-wrap .data-table
     11  Forms              .form-field .form-input .form-select ... .input-group
     12  Feedback           .alert .toast .empty-state .spinner .progress .skeleton
     13  Overlays           .modal
     14  Navigation bits    .segmented .tabs .steps
     15  Data display       .stat-row .definition-list
     16  Utility escape hatches (deliberately tiny)
     17  Responsive breakpoint overrides
     18  Dense page         .page--dense (compact reporting screens)
     19  Fit-to-screen page .page--fit .page-fit__panel (no page scroll)
     20  Sign-in screen     .auth-screen .auth-layout .auth-brand .auth-panel
     21  Space savers       .filter-bar .note-fold .page--tight .cell-when
     22  Receipt line       .line-grid--receipt .line-grid__derived
   ========================================================================= */


/* ==========================================================================
   00  RESET & BASE
   A small, opinionated reset. Removes UA margins, makes sizing predictable,
   and sets the document's typographic baseline from the tokens.
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd, ol, ul, pre {
  margin: 0;
}

ol[class], ul[class] { list-style: none; padding: 0; }

html, body { height: 100%; }

body {
  font-family: var(--font-sans);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-secondary);
  background-color: var(--color-canvas);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overscroll-behavior: none;
}

img, svg, video, canvas { display: block; max-width: 100%; }

input, button, textarea, select {
  font: inherit;
  color: inherit;
  margin: 0;
}

button { background: none; border: 0; padding: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

table { border-collapse: collapse; border-spacing: 0; width: 100%; }

hr { border: 0; }

::selection {
  background: var(--color-primary-soft);
  color: var(--color-primary-text-strong);
}

/* Electron chrome: hide the OS scrollbar but keep the area scrollable.
   Use .scroll-area (section 03) on any pane that needs to scroll. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--color-border-strong);
  border-radius: var(--radius-pill);
  border: 3px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--color-text-subtle); background-clip: content-box; }

/* Global keyboard focus treatment. Mouse clicks do not trigger it. */
:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

@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;
  }
}


/* ==========================================================================
   01  DESIGN TOKENS
   Everything below this block is built from these values. Change a colour or
   a spacing step HERE and it propagates through every component.
   ========================================================================== */

:root {

  /* ---- 1.1  Brand palette -------------------------------------------------
     THE BUNHOUSE CAFE — taken from the cafe's own emblem and its brand sheet
     (the_bunhouse_caf_css_theme_tokens): deep artisanal olive, warm roasted
     gold, ivory linen.

     Primary   olive : the brand, every primary action, healthy states
     Secondary gold  : the emblem's laurel — attention, "needs a look"
     Tertiary  slate blue : informational only, never a brand colour
     Danger    red   : aborts, discrepancies, destructive actions

     Where the brand sheet gives one value and the system needs a family (a
     brighter step for chart bars, a solid border where the sheet has an alpha,
     a darker text step that stays readable at 11px), the extra step is derived
     from the brand colour and marked "derived" below.                        */

  --color-primary:              #384f2e;  /* olive — the brand colour         */
  --color-primary-hover:        #2b3d23;  /* button hover                     */
  --color-primary-active:       #1f2c19;  /* button pressed                   */
  --color-primary-strong:       #4c6a3c;  /* derived — bars, dots, gauges     */
  --color-primary-soft:         #ebf1e8;  /* chip / alert background          */
  --color-primary-tint:         #f4f8f2;  /* derived — focused input tint     */
  --color-primary-border:       #cbd8c3;  /* derived from the 20% olive rule  */
  --color-primary-text:         #384f2e;  /* text on a soft olive background  */
  --color-primary-text-strong:  #1f2c19;  /* selection text                   */
  --color-on-primary:           #faf7f2;  /* ivory, not white                 */

  --color-secondary:            #c49859;  /* roasted gold                     */
  --color-secondary-hover:      #af8445;
  --color-secondary-strong:     #d3ab72;  /* derived — dots, bars             */
  --color-secondary-soft:       #fbf5eb;
  --color-secondary-soft-alt:   #f5e7d0;  /* derived                          */
  --color-secondary-border:     #e7d3b0;  /* derived from the 30% gold rule   */
  --color-secondary-text:       #8c6a36;  /* derived — the sheet's #af8445 is
                                             too light to read at label size  */
  --color-on-secondary:         #1c1917;  /* the sheet's text-on-accent       */

  --color-tertiary:             #46697c;  /* slate blue                       */
  --color-tertiary-hover:       #395768;  /* derived                          */
  --color-tertiary-strong:      #5d8499;  /* derived                          */
  --color-tertiary-soft:        #edf4f7;
  --color-tertiary-soft-alt:    #dbe8ef;  /* derived                          */
  --color-tertiary-border:      #bcd2dd;  /* derived                          */
  --color-tertiary-text:        #35505e;  /* derived                          */
  --color-on-tertiary:          #ffffff;

  --color-danger:               #b94a48;
  --color-danger-hover:         #9c3c3a;  /* derived                          */
  --color-danger-soft:          #fdf2f2;
  --color-danger-soft-alt:      #f9e3e2;  /* derived                          */
  --color-danger-border:        #efc9c8;  /* derived                          */
  --color-danger-text:          #8e3634;  /* derived                          */
  --color-on-danger:            #ffffff;

  /* Aliases so status language reads naturally in component code. The brand
     sheet maps success onto the olive and warning onto the gold, which is what
     these already do. */
  --color-success:              var(--color-primary);
  --color-success-soft:         var(--color-primary-soft);
  --color-success-border:       var(--color-primary-border);
  --color-success-text:         var(--color-primary-text);
  --color-warning:              var(--color-secondary);
  --color-warning-soft:         var(--color-secondary-soft);
  --color-warning-border:       var(--color-secondary-border);
  --color-warning-text:         var(--color-secondary-text);
  --color-info:                 var(--color-tertiary);
  --color-info-soft:            var(--color-tertiary-soft);
  --color-info-border:          var(--color-tertiary-border);
  --color-info-text:            var(--color-tertiary-text);

  /* ---- 1.1b  House marks -------------------------------------------------
     The sign-in screen and the sidebar lockup, where the identity speaks at
     full volume. The olive and gold are the brand sheet's own; the cream is
     the one the logo file is actually printed on, sampled from the image —
     it has to match to the value or the picture shows its edges.           */
  --brand-cream:                #fef7ed;  /* assets/logo.png's background    */
  --brand-cream-deep:           #f2e7d5;  /* its shaded edge                 */
  --brand-olive:                #384f2e;  /* = --color-primary               */
  --brand-olive-deep:           #1f2c19;  /* pressed / backdrop              */
  --brand-olive-soft:           #4c6a3c;  /* hover, secondary lines          */
  --brand-gold:                 #c49859;  /* = --color-secondary             */
  --brand-gold-soft:            #e7d3b0;  /* hairlines on cream              */

  /* Aliases so status language reads naturally in component code. */
  --color-success:              var(--color-primary);
  --color-success-soft:         var(--color-primary-soft);
  --color-success-border:       var(--color-primary-border);
  --color-success-text:         var(--color-primary-text);
  --color-warning:              var(--color-secondary);
  --color-warning-soft:         var(--color-secondary-soft);
  --color-warning-border:       var(--color-secondary-border);
  --color-warning-text:         var(--color-secondary-text);
  --color-info:                 var(--color-tertiary);
  --color-info-soft:            var(--color-tertiary-soft);
  --color-info-border:          var(--color-tertiary-border);
  --color-info-text:            var(--color-tertiary-text);

  /* ---- 1.2  Neutral ramp -------------------------------------------------
     Warm stone rather than cold slate, so a neutral chip or a disabled control
     sits on the ivory canvas instead of on top of it. The token NAMES keep the
     word "slate" because the whole file refers to them; only the values are
     the cafe's.                                                             */
  --color-slate-50:             #faf7f2;
  --color-slate-100:            #f3efe7;
  --color-slate-200:            #e8e2d5;
  --color-slate-300:            #d3ccbe;
  --color-slate-400:            #a69d8e;
  --color-slate-500:            #847c70;
  --color-slate-600:            #6b6459;
  --color-slate-700:            #575249;
  --color-slate-800:            #3a3630;
  --color-slate-900:            #211e1a;

  /* ---- 1.3  Surfaces ------------------------------------------------------
     canvas  = the page behind everything
     surface = a card / module sitting on the canvas
     sunken  = nested container, table header, inset "workbench" strips      */
  --color-canvas:               #faf7f2;  /* warm ivory linen                 */
  --color-surface:              #ffffff;
  --color-surface-raised:       #ffffff;
  --color-surface-sunken:       #f3efe7;  /* clotted cream                    */
  --color-surface-hover:        #faf7f2;
  --color-surface-active:       #f3efe7;
  --color-surface-inverse:      #211e1a;  /* deep espresso                    */
  --color-surface-translucent:  rgba(255, 255, 255, 0.95);

  /* ---- 1.4  Borders & dividers ------------------------------------------ */
  --color-border:               #e8e2d5;  /* default structural 1px line      */
  --color-border-subtle:        #f1ece2;  /* table row dividers, card inners  */
  --color-border-strong:        #d5cca8;  /* form controls, emphasised edges  */
  --color-border-hover:         #d5cca8;  /* card hover edge                  */

  /* ---- 1.5  Text --------------------------------------------------------- */
  --color-text:                 #211e1a;  /* deep espresso — headings, figures */
  --color-text-secondary:       #575249;  /* body copy, supporting metadata   */
  --color-text-muted:           #736c60;  /* labels, units, inactive — the brand
                                             sheet's #847c70 darkened one step:
                                             at 11px on ivory it measured 3.9:1,
                                             under the 4.5:1 a label needs      */
  --color-text-subtle:          #a69d8e;  /* placeholders, disabled, icons    */
  --color-text-inverse:         #faf7f2;
  --color-text-link:            var(--color-primary-text);

  /* ---- 1.6  Focus & overlay --------------------------------------------- */
  --color-focus:                #384f2e;
  --color-focus-ring:           rgba(56, 79, 46, 0.18);
  --color-focus-tint:           #f4f8f2;
  --color-backdrop:             rgba(33, 30, 26, 0.28);

  /* ---- 1.7  Font families ------------------------------------------------ */
  --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI',
               Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas,
               'Liberation Mono', monospace;
  /* The emblem's lettering is a serif, so the TITLES are too — page titles,
     card titles, modal titles and the sign-in greeting, and nothing else.
     Body copy, labels, tables and figures stay sans and mono, which is what
     keeps a dense screen readable. */
  --font-serif: 'Playfair Display', Georgia, 'Times New Roman', serif;

  /* ---- 1.8  Font weights ------------------------------------------------- */
  --font-weight-regular:  400;
  --font-weight-medium:   500;
  --font-weight-semibold: 600;
  --font-weight-bold:     700;

  /* ---- 1.9  Type scale ---------------------------------------------------
     Values taken from the rendered reference (code.html tailwind.config),
     plus display-lg / metric-val from the written spec.                     */

  /* display-lg — hero numbers only, almost never used in app chrome */
  --font-size-display-lg:        3rem;      /* 48px */
  --line-height-display-lg:      3.5rem;    /* 56px */
  --letter-spacing-display-lg:   -0.025em;
  --font-weight-display-lg:      var(--font-weight-bold);

  /* headline-xl — the one <h1> per page */
  --font-size-headline-xl:       2rem;      /* 32px */
  --line-height-headline-xl:     2.5rem;    /* 40px */
  --letter-spacing-headline-xl:  -0.02em;
  --font-weight-headline-xl:     var(--font-weight-bold);

  /* headline-xl-mobile — automatic swap for .page-title under 768px */
  --font-size-headline-xl-mobile:      1.5rem;  /* 24px */
  --line-height-headline-xl-mobile:    2rem;    /* 32px */
  --letter-spacing-headline-xl-mobile: -0.01em;

  /* headline-lg — section headings (<h2>) */
  --font-size-headline-lg:       1.5rem;    /* 24px */
  --line-height-headline-lg:     2rem;      /* 32px */
  --letter-spacing-headline-lg:  -0.015em;
  --font-weight-headline-lg:     var(--font-weight-semibold);

  /* headline-md — card titles, modal titles (<h3>) */
  --font-size-headline-md:       1.25rem;   /* 20px */
  --line-height-headline-md:     1.75rem;   /* 28px */
  --letter-spacing-headline-md:  -0.01em;
  --font-weight-headline-md:     var(--font-weight-semibold);

  /* headline-sm — sub-headings, button text, strong table cells */
  --font-size-headline-sm:       1rem;      /* 16px */
  --line-height-headline-sm:     1.5rem;    /* 24px */
  --letter-spacing-headline-sm:  0;
  --font-weight-headline-sm:     var(--font-weight-semibold);

  /* title-sm — dense strong text inside rows and chips' siblings */
  --font-size-title-sm:          0.8125rem; /* 13px */
  --line-height-title-sm:        1.125rem;  /* 18px */
  --letter-spacing-title-sm:     -0.005em;
  --font-weight-title-sm:        var(--font-weight-semibold);

  /* body-lg / body-md / body-sm */
  --font-size-body-lg:           1rem;      /* 16px */
  --line-height-body-lg:         1.5rem;    /* 24px */
  --font-size-body-md:           0.875rem;  /* 14px — app default */
  --line-height-body-md:         1.25rem;   /* 20px */
  --font-size-body-sm:           0.75rem;   /* 12px */
  --line-height-body-sm:         1rem;      /* 16px */

  /* label-caps — uppercase overlines, table headers, chip text, KPI labels */
  --font-size-label-caps:        0.6875rem; /* 11px */
  --line-height-label-caps:      0.875rem;  /* 14px */
  --letter-spacing-label-caps:   0.06em;
  --font-weight-label-caps:      var(--font-weight-bold);

  /* label-md — form labels when sentence case is wanted */
  --font-size-label-md:          0.8125rem; /* 13px */
  --line-height-label-md:        1rem;      /* 16px */
  --letter-spacing-label-md:     0.025em;
  --font-weight-label-md:        var(--font-weight-semibold);

  /* metric-val — the big figure in a metric card */
  --font-size-metric-val:        1.75rem;   /* 28px */
  --line-height-metric-val:      2rem;      /* 32px */
  --letter-spacing-metric-val:   -0.03em;
  --font-weight-metric-val:      var(--font-weight-bold);

  /* numeric ladder — JetBrains Mono, tabular. Money, temps, timestamps, IDs. */
  --font-size-numeric-lg:        1.25rem;   /* 20px */
  --line-height-numeric-lg:      1.75rem;   /* 28px */
  --letter-spacing-numeric-lg:   -0.02em;
  --font-weight-numeric-lg:      var(--font-weight-semibold);
  --font-size-numeric-md:        0.875rem;  /* 14px */
  --line-height-numeric-md:      1.25rem;   /* 20px */
  --font-weight-numeric-md:      var(--font-weight-medium);
  --font-size-numeric-sm:        0.75rem;   /* 12px */
  --line-height-numeric-sm:      1rem;      /* 16px */
  --font-weight-numeric-sm:      var(--font-weight-medium);

  /* Tabular-figure feature string. Applied to every numeric class. */
  --font-feature-tnum: "tnum" 1, "lnum" 1;

  /* ---- 1.10  Radii -------------------------------------------------------
     xs  4px  micro badges, kbd, swatches
     sm  6px  segmented options, nested pills
     md 10px  buttons, inputs, selects, chips, small surfaces
     lg 16px  cards, panels, modals, table wrappers
     xl 24px  large feature panels
     pill     status pills & connection dots only                            */
  --radius-xs:    0.25rem;   /*  4px */
  --radius-sm:    0.375rem;  /*  6px — the brand sheet's sm                 */
  --radius-md:    0.625rem;  /* 10px — its md: buttons, inputs, chips       */
  --radius-lg:    1rem;      /* 16px — its lg: cards, panels, modals        */
  --radius-xl:    1.5rem;    /* 24px — its xl: feature panels               */
  --radius-2xl:   2rem;      /* 32px */
  --radius-pill:  9999px;
  --radius-circle: 50%;

  /* ---- 1.11  Spacing scale (8px rhythm with a 4px half-step) ------------- */
  --space-2xs:  0.125rem;  /*  2px */
  --space-xs:   0.25rem;   /*  4px */
  --space-sm:   0.5rem;    /*  8px */
  --space-md:   0.75rem;   /* 12px */
  --space-base: 1rem;      /* 16px */
  --space-lg:   1.5rem;    /* 24px */
  --space-xl:   2rem;      /* 32px */
  --space-2xl:  3rem;      /* 48px */
  --space-3xl:  4rem;      /* 64px */

  /* Grid gutters and page margins, per breakpoint (see section 17). */
  --gutter:               1rem;    /* mobile  16px */
  --gutter-tablet:        1.25rem; /* tablet  20px */
  --gutter-desktop:       1.5rem;  /* desktop 24px */
  --page-margin:          1rem;    /* mobile  16px */
  --page-margin-tablet:   1.5rem;  /* tablet  24px */
  --page-margin-desktop:  2rem;    /* desktop 32px */

  /* Live values — section 17 rewrites these at each breakpoint so components
     can simply use var(--grid-gutter) / var(--page-gutter). */
  --grid-gutter: var(--gutter);
  --page-gutter: var(--page-margin);
  --grid-columns: 4;

  /* ---- 1.12  Elevation ---------------------------------------------------
     Whispering ambient shadows tinted with espresso (#211e1a), never neutral
     black and never cold — a cool shadow on an ivory canvas reads as grey.  */
  --shadow-xs:         0 1px 2px 0 rgba(33, 30, 26, 0.04);
  --shadow-card:       0 2px 4px 0 rgba(33, 30, 26, 0.05),
                       0 1px 2px 0 rgba(33, 30, 26, 0.03);
  --shadow-card-hover: 0 4px 12px 0 rgba(33, 30, 26, 0.07),
                       0 2px 4px 0 rgba(33, 30, 26, 0.04);
  --shadow-popover:    0 12px 24px -4px rgba(33, 30, 26, 0.09),
                       0 4px 8px 0 rgba(33, 30, 26, 0.04);
  --shadow-overlay:    0 20px 40px -8px rgba(33, 30, 26, 0.14),
                       0 8px 16px -8px rgba(33, 30, 26, 0.08);
  --shadow-inset:      inset 0 1px 2px 0 rgba(33, 30, 26, 0.05);
  --shadow-focus:      0 0 0 2px var(--color-focus-ring);

  /* ---- 1.13  Control metrics -------------------------------------------- */
  --control-height-sm: 2rem;    /* 32px — .btn--sm, inline filters           */
  --control-height:    2.5rem;  /* 40px — buttons, inputs, selects           */
  --control-height-lg: 3rem;    /* 48px — primary CTAs on touch stations     */
  --table-row-height:  3rem;    /* 48px — .data-table rows                   */
  --chip-height:       1.5rem;  /* 24px                                      */
  --sidebar-width:     16rem;   /* 256px                                     */
  --sidebar-rail-width: 4.5rem; /* 72px  — collapsed rail under 1024px       */
  --topbar-height:     4rem;    /* 64px                                      */
  --border-width:      1px;
  --border-width-thick: 2px;
  --focus-ring-width:  2px;

  /* ---- 1.14  Motion ------------------------------------------------------ */
  --duration-fast:   150ms;  /* the brand sheet's transition-fast           */
  --duration-base:   220ms;  /* its transition-base                         */
  --duration-slow:   320ms;
  --easing-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --easing-emphasis: cubic-bezier(0.2, 0, 0, 1);
  --transition-colors: color var(--duration-fast) var(--easing-standard),
                       background-color var(--duration-fast) var(--easing-standard),
                       border-color var(--duration-fast) var(--easing-standard),
                       box-shadow var(--duration-fast) var(--easing-standard);

  /* ---- 1.15  Stacking order --------------------------------------------- */
  --z-base:     0;
  --z-sticky:   10;
  --z-sidebar:  50;
  --z-topbar:   40;
  --z-dropdown: 100;
  --z-backdrop: 900;
  --z-modal:    1000;
  --z-toast:    1100;
}


/* ==========================================================================
   02  TYPOGRAPHY CLASSES
   Put ONE of these on a text element. They carry size + line-height +
   tracking + weight together, so you never re-declare them per screen.
   ========================================================================== */

.text-display-lg {
  font-family: var(--font-sans);
  font-size: var(--font-size-display-lg);
  line-height: var(--line-height-display-lg);
  letter-spacing: var(--letter-spacing-display-lg);
  font-weight: var(--font-weight-display-lg);
  color: var(--color-text);
}

.text-headline-xl {
  font-family: var(--font-sans);
  font-size: var(--font-size-headline-xl);
  line-height: var(--line-height-headline-xl);
  letter-spacing: var(--letter-spacing-headline-xl);
  font-weight: var(--font-weight-headline-xl);
  color: var(--color-text);
}

.text-headline-lg {
  font-family: var(--font-sans);
  font-size: var(--font-size-headline-lg);
  line-height: var(--line-height-headline-lg);
  letter-spacing: var(--letter-spacing-headline-lg);
  font-weight: var(--font-weight-headline-lg);
  color: var(--color-text);
}

.text-headline-md {
  font-family: var(--font-sans);
  font-size: var(--font-size-headline-md);
  line-height: var(--line-height-headline-md);
  letter-spacing: var(--letter-spacing-headline-md);
  font-weight: var(--font-weight-headline-md);
  color: var(--color-text);
}

.text-headline-sm {
  font-family: var(--font-sans);
  font-size: var(--font-size-headline-sm);
  line-height: var(--line-height-headline-sm);
  letter-spacing: var(--letter-spacing-headline-sm);
  font-weight: var(--font-weight-headline-sm);
  color: var(--color-text);
}

.text-title-sm {
  font-family: var(--font-sans);
  font-size: var(--font-size-title-sm);
  line-height: var(--line-height-title-sm);
  letter-spacing: var(--letter-spacing-title-sm);
  font-weight: var(--font-weight-title-sm);
  color: var(--color-text);
}

.text-body-lg {
  font-size: var(--font-size-body-lg);
  line-height: var(--line-height-body-lg);
  font-weight: var(--font-weight-regular);
}

.text-body-md {
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  font-weight: var(--font-weight-regular);
}

.text-body-sm {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  font-weight: var(--font-weight-regular);
}

/* Uppercase overline. Batch status markers, sensor IDs, origin tags,
   KPI labels, table headers. */
.text-label-caps,
.label-caps {
  font-family: var(--font-sans);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-label-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.text-label-md {
  font-size: var(--font-size-label-md);
  line-height: var(--line-height-label-md);
  letter-spacing: var(--letter-spacing-label-md);
  font-weight: var(--font-weight-label-md);
  color: var(--color-text-secondary);
}

/* --- Numeric text. ALWAYS use one of these for a number the operator will
       compare down a column: money, weights, temps, ROR, times, IDs. ------- */
.num,
.text-numeric-md {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-md);
  line-height: var(--line-height-numeric-md);
  font-weight: var(--font-weight-numeric-md);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
}

.text-numeric-lg {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-lg);
  line-height: var(--line-height-numeric-lg);
  letter-spacing: var(--letter-spacing-numeric-lg);
  font-weight: var(--font-weight-numeric-lg);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
}

.text-numeric-sm {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  line-height: var(--line-height-numeric-sm);
  font-weight: var(--font-weight-numeric-sm);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
}

/* Tabular figures WITHOUT switching family — for sans-serif figures that must
   still align (metric-card values, stepper timestamps). */
.tnum {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
}

/* Semantic text colours. */
.text-default   { color: var(--color-text); }
.text-secondary { color: var(--color-text-secondary); }
.text-muted     { color: var(--color-text-muted); }
.text-subtle    { color: var(--color-text-subtle); }
.text-primary   { color: var(--color-primary-text); }
.text-warning   { color: var(--color-warning-text); }
.text-info      { color: var(--color-info-text); }
.text-danger    { color: var(--color-danger); }

/* Inline link inside body copy. */
.link {
  color: var(--color-text-link);
  font-weight: var(--font-weight-semibold);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: var(--transition-colors);
}
.link:hover { border-bottom-color: currentColor; }


/* ==========================================================================
   03  LAYOUT PRIMITIVES
   Four small tools that replace 90% of ad-hoc flex markup.
     .stack      — vertical rhythm, children separated by one gap
     .cluster    — horizontal row, wraps, vertically centred
     .divider    — a 1px rule
     .scroll-area— a pane that scrolls on its own
   ========================================================================== */

/* --- .stack : vertical flow ---------------------------------------------- */
.stack        { display: flex; flex-direction: column; gap: var(--space-base); min-width: 0; }
.stack--2xs   { gap: var(--space-2xs); }
.stack--xs    { gap: var(--space-xs); }
.stack--sm    { gap: var(--space-sm); }
.stack--md    { gap: var(--space-md); }
.stack--lg    { gap: var(--space-lg); }
.stack--xl    { gap: var(--space-xl); }

/* --- .cluster : horizontal flow ------------------------------------------ */
.cluster          { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); min-width: 0; }
.cluster--2xs     { gap: var(--space-2xs); }
.cluster--xs      { gap: var(--space-xs); }
.cluster--sm      { gap: var(--space-sm); }   /* same as the default; explicit */
.cluster--md      { gap: var(--space-md); }
.cluster--lg      { gap: var(--space-lg); }
.cluster--between { justify-content: space-between; width: 100%; }
.cluster--end     { justify-content: flex-end; width: 100%; }
.cluster--center  { justify-content: center; width: 100%; }
.cluster--top     { align-items: flex-start; }
.cluster--baseline{ align-items: baseline; }
.cluster--nowrap  { flex-wrap: nowrap; }
.cluster__spacer  { flex: 1 1 auto; }   /* push the rest of the row right */

/* --- .divider ------------------------------------------------------------ */
.divider {
  border: 0;
  border-top: var(--border-width) solid var(--color-border);
  width: 100%;
  margin: 0;
}
.divider--subtle   { border-top-color: var(--color-border-subtle); }
.divider--vertical {
  border-top: 0;
  border-left: var(--border-width) solid var(--color-border);
  width: 0;
  align-self: stretch;
  min-height: 1.25rem;
}
.divider--spaced { margin-block: var(--space-md); }

/* --- .scroll-area : bounded scroller -------------------------------------
   Give it a height from a parent (flex/grid) or use a --max modifier.      */
.scroll-area {
  overflow: auto;
  min-height: 0;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
.scroll-area--x    { overflow-x: auto; overflow-y: hidden; }
.scroll-area--y    { overflow-y: auto; overflow-x: hidden; }
.scroll-area--sm   { max-height: 14rem; }
.scroll-area--md   { max-height: 22rem; }
.scroll-area--lg   { max-height: 32rem; }


/* ==========================================================================
   04  RESPONSIVE GRID
   .layout-grid is the canonical grid: 12 columns on desktop (>=1024px),
   8 on tablet (768-1023px), 4 on mobile (<768px). Gutters come from the
   breakpoint tokens. Children take .col-span-N.

   Spans larger than the available column count clamp to full width, so
   .col-span-7 is 7/12 on desktop and full width on tablet and mobile.
   ========================================================================== */

.layout-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gutter);
  align-items: start;
  width: 100%;
}

.col-span-1  { grid-column: span 1  / span 1;  }
.col-span-2  { grid-column: span 2  / span 2;  }
.col-span-3  { grid-column: span 3  / span 3;  }
.col-span-4  { grid-column: span 4  / span 4;  }
.col-span-5  { grid-column: span 5  / span 5;  }
.col-span-6  { grid-column: span 6  / span 6;  }
.col-span-7  { grid-column: span 7  / span 7;  }
.col-span-8  { grid-column: span 8  / span 8;  }
.col-span-9  { grid-column: span 9  / span 9;  }
.col-span-10 { grid-column: span 10 / span 10; }
.col-span-11 { grid-column: span 11 / span 11; }
.col-span-12 { grid-column: span 12 / span 12; }
.col-span-full { grid-column: 1 / -1; }

/* --- Fixed-count grids for card walls ------------------------------------
   These collapse automatically: 4 -> 2 -> 1, 3 -> 2 -> 1, 2 -> 1.          */
.grid-2, .grid-3, .grid-4, .kpi-grid {
  display: grid;
  gap: var(--grid-gutter);
  width: 100%;
  align-items: stretch;
}
.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)); }

/* .kpi-grid — the metric-card strip at the top of a page. */
.kpi-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-md); }
.kpi-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kpi-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Auto-fit wall for supplier / ingredient cards of unknown count. */
.grid-auto {
  display: grid;
  gap: var(--grid-gutter);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}


/* ==========================================================================
   05  APP SHELL
   The persistent chrome: fixed sidebar on the left, sticky topbar, scrolling
   main region. Wrap the whole <body> content in .app-shell.

     <div class="app-shell">
       <aside class="app-sidebar"> ... </aside>
       <div class="app-main">
         <header class="app-topbar"> ... </header>
         <main class="app-content"> ... </main>
       </div>
     </div>
   ========================================================================== */

.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--color-canvas);
}

/* --- Sidebar -------------------------------------------------------------- */
.app-sidebar {
  position: sticky;
  top: 0;
  z-index: var(--z-sidebar);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-lg);
  height: 100vh;
  padding-block: var(--space-base);
  background: var(--color-surface);
  border-right: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-xs);
  overflow: hidden auto;
}

/* Brand lockup at the top of the sidebar. */
.app-sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding-inline: var(--space-base);
  min-height: 2rem;
}
.app-sidebar__brand-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  background: var(--color-primary-soft);
  border: var(--border-width) solid var(--color-primary-border);
  color: var(--color-primary-text);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-body-sm);
}
.app-sidebar__brand-text { display: flex; flex-direction: column; min-width: 0; }
.app-sidebar__brand-name {
  font-size: var(--font-size-headline-sm);
  font-weight: var(--font-weight-semibold);
  letter-spacing: -0.01em;
  line-height: 1.1;
  color: var(--color-text);
}
.app-sidebar__brand-sub {
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-top: var(--space-2xs);
}

/* Nav groups. */
.app-sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-inline: var(--space-sm);
}
.app-sidebar__section-label {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-primary-text);
}
.app-sidebar__top    { display: flex; flex-direction: column; gap: var(--space-lg); min-height: 0; }
.app-sidebar__footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-inline: var(--space-sm);
}
/* Small version / station strip pinned to the very bottom. */
.app-sidebar__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
  padding: var(--space-sm) var(--space-md) 0;
  border-top: var(--border-width) solid var(--color-border-subtle);
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text-subtle);
}

/* --- .nav-item : one sidebar link ----------------------------------------
   Add .is-active (and aria-current="page") to the current route.           */
.nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  border-right: var(--border-width-thick) solid transparent;
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
  cursor: pointer;
  text-align: left;
  width: 100%;
  transition: var(--transition-colors);
}
.nav-item:hover {
  background: var(--color-surface-sunken);
  color: var(--color-text);
}
.nav-item:hover .nav-item__icon { color: var(--color-text-muted); }
.nav-item.is-active {
  background: var(--color-primary-soft);
  color: var(--color-primary-text);
  font-weight: var(--font-weight-semibold);
  border-right-color: var(--color-primary);
}
.nav-item.is-active .nav-item__icon { color: var(--color-primary); }
.nav-item.is-disabled,
.nav-item[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}
.nav-item__icon  { flex: 0 0 auto; color: var(--color-text-subtle); font-size: 20px; }
.nav-item__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-item__badge {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text-subtle);
}
/* --- .picker-item : a row in a list you choose FROM inside a page ----------
     The vendor list on Vendor Raw Material Mapping, and anything like it. It looks like a
     nav row but is not one: .nav-item belongs to the sidebar and follows the sidebar's
     collapsed state, which would blank a list sitting in the page.

       <button class="picker-item is-active">
         <span class="picker-item__label">Bhakti Marketing</span>
         <span class="picker-item__badge">14</span>
       </button>                                                              */
.picker-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-md);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  text-align: left;
  cursor: pointer;
  transition: var(--transition-colors);
}
.picker-item:hover { background: var(--color-surface-sunken); color: var(--color-text); }
.picker-item.is-active {
  background: var(--color-primary-soft);
  color: var(--color-primary-text);
  font-weight: var(--font-weight-semibold);
}
.picker-item__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Two lines when the row needs a qualifier (a roster's dates under its name) — the name
   keeps the width it needs instead of being squeezed by a long badge. */
.picker-item--stack { flex-direction: column; align-items: flex-start; gap: 0; }
.picker-item__sub {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-muted);
  font-weight: var(--font-weight-regular);
}
.picker-item__badge {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text-subtle);
}

/* Compact secondary links ("Station Tools" group). */
.nav-item--sm { font-size: var(--font-size-body-sm); line-height: var(--line-height-body-sm); }
.nav-item--sm .nav-item__icon { font-size: 18px; }

/* --- Topbar --------------------------------------------------------------- */
.app-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 100vh;
  background: var(--color-canvas);
}

.app-topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-topbar);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  height: var(--topbar-height);
  flex: 0 0 var(--topbar-height);
  padding-inline: var(--space-lg);
  background: var(--color-surface-translucent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-xs);
  overflow: hidden;   /* the bar never scrolls; hide overflow instead */
}
.app-topbar__start  { display: flex; align-items: center; gap: var(--space-lg); min-width: 0; flex: 0 1 auto; overflow: hidden; }
.app-topbar__center { flex: 1 1 auto; max-width: 28rem; margin-inline: var(--space-lg); min-width: 0; }
.app-topbar__end    { display: flex; align-items: center; gap: var(--space-md); flex: 0 0 auto; }

/* Location / context switcher pill that lives in .app-topbar__start. */
.context-switch {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  border: var(--border-width) solid var(--color-border);
  cursor: pointer;
  transition: var(--transition-colors);
  text-align: left;
}
.context-switch:hover { background: var(--color-border); }
.context-switch__text { display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.context-switch__label {
  display: block;
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.context-switch__value {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--font-size-title-sm);
  line-height: var(--line-height-title-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

/* Live status pill for the topbar ("Live Sync: 3 stations active"). */
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--radius-pill);
  background: var(--color-primary-soft);
  border: var(--border-width) solid var(--color-primary-border);
  color: var(--color-primary-text);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
}
.status-pill--warning { background: var(--color-warning-soft); border-color: var(--color-warning-border); color: var(--color-warning-text); }
.status-pill--info    { background: var(--color-info-soft);    border-color: var(--color-info-border);    color: var(--color-info-text); }
.status-pill--neutral { background: var(--color-surface-sunken); border-color: var(--color-border); color: var(--color-text-muted); }

/* --- Main content region -------------------------------------------------- */
.app-content {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--space-lg) var(--page-gutter) var(--space-2xl);
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
.app-content--flush { padding: 0; }
.app-content--narrow { max-width: 68rem; width: 100%; margin-inline: auto; }


/* ==========================================================================
   06  PAGE HEADER
   The block at the top of every screen: breadcrumb, H1, one-line description,
   and the page-level action buttons on the right.
   ========================================================================== */

.page-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-bottom: var(--space-lg);
}
.page-header__text    { display: flex; flex-direction: column; gap: var(--space-xs); min-width: 0; }
.page-header__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); }

/* The one <h1> per page, set in the emblem's serif and its olive — the single
   loudest piece of brand on a working screen. Everything below it stays sans. */
.page-title {
  font-family: var(--font-serif);
  font-size: var(--font-size-headline-xl);
  line-height: var(--line-height-headline-xl);
  letter-spacing: var(--letter-spacing-headline-xl);
  font-weight: var(--font-weight-headline-xl);
  color: var(--color-primary);
}

.page-subtitle {
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  color: var(--color-text-secondary);
  max-width: 44rem;
}

/* --- .breadcrumb : uppercase context trail above the title ---------------- */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.breadcrumb__item    { color: var(--color-text-muted); transition: var(--transition-colors); }
.breadcrumb__item:hover { color: var(--color-text); }
.breadcrumb__sep     { color: var(--color-border-strong); }
.breadcrumb__current { color: var(--color-primary-text); }

/* --- Section header inside a page ---------------------------------------- */
.section-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}
.section-header__text { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }
.section-title {
  font-size: var(--font-size-headline-lg);
  line-height: var(--line-height-headline-lg);
  letter-spacing: var(--letter-spacing-headline-lg);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
}
.section-subtitle {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-secondary);
}


/* ==========================================================================
   07  BUTTONS
     .btn                base — never used alone, always with a variant
     .btn--primary       the single main action on a screen (olive)
     .btn--secondary     the default neutral action (white + border)
     .btn--destructive   aborts, deletes, discrepancy reports
     .btn--warning       amber "needs a decision" actions (approve drafts)
     .btn--tertiary      sky, telemetry/inspection actions
     .btn--ghost         low-emphasis, no border until hover
     .btn--link          looks like text, behaves like a button
   Modifiers: .btn--sm  .btn--lg  .btn--icon  .btn--block
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  height: var(--control-height);
  padding-inline: var(--space-base);
  border-radius: var(--radius-md);
  border: var(--border-width) solid transparent;
  font-family: var(--font-sans);
  font-size: var(--font-size-title-sm);
  line-height: 1;
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition: var(--transition-colors), transform var(--duration-fast) var(--easing-standard);
}
.btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.btn:active { transform: translateY(0.5px); }
.btn:disabled,
.btn.is-disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
}

/* Primary — olive fill. One per view. */
.btn--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-color: var(--color-primary);
  box-shadow: var(--shadow-card);
}
.btn--primary:hover  { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
.btn--primary:active { background: var(--color-primary-active); border-color: var(--color-primary-active); }

/* Secondary — white surface with a structural border. The workhorse. */
.btn--secondary {
  background: var(--color-surface);
  color: var(--color-text-secondary);
  border-color: var(--color-border);
  box-shadow: var(--shadow-xs);
}
.btn--secondary:hover  { background: var(--color-surface-hover); color: var(--color-text); border-color: var(--color-border-hover); }
.btn--secondary:active { background: var(--color-surface-active); }

/* Destructive — soft red, never a solid red fill. */
.btn--destructive {
  background: var(--color-danger-soft);
  color: var(--color-danger);
  border-color: var(--color-danger-border);
}
.btn--destructive:hover  { background: var(--color-danger-soft-alt); color: var(--color-danger-hover); }
.btn--destructive:active { background: var(--color-danger-soft-alt); }

/* Warning — amber, for "approve / review" style actions. */
.btn--warning {
  background: var(--color-secondary-soft);
  color: var(--color-secondary-text);
  border-color: var(--color-secondary-border);
}
.btn--warning:hover { background: var(--color-secondary-soft-alt); }

/* Tertiary — sky, telemetry & inspection. */
.btn--tertiary {
  background: var(--color-tertiary-soft);
  color: var(--color-tertiary-text);
  border-color: var(--color-tertiary-border);
}
.btn--tertiary:hover { background: var(--color-tertiary-soft-alt); }

/* Ghost — transparent until hovered. Toolbars, table row actions. */
.btn--ghost {
  background: transparent;
  color: var(--color-text-muted);
  border-color: transparent;
}
.btn--ghost:hover { background: var(--color-surface-sunken); color: var(--color-text); }

/* Link — no chrome at all. */
.btn--link {
  height: auto;
  padding: 0;
  background: transparent;
  border-color: transparent;
  color: var(--color-primary-text);
}
.btn--link:hover { color: var(--color-primary-active); text-decoration: underline; }

/* --- Size / shape modifiers ---------------------------------------------- */
.btn--sm {
  height: var(--control-height-sm);
  padding-inline: var(--space-md);
  font-size: var(--font-size-body-sm);
  gap: var(--space-2xs);
}
.btn--lg {
  height: var(--control-height-lg);
  padding-inline: var(--space-lg);
  font-size: var(--font-size-headline-sm);
}
/* Square icon-only button. Pair with a variant and always give aria-label. */
.btn--icon {
  width: var(--control-height);
  height: var(--control-height);
  padding: 0;
  flex: 0 0 auto;
}
.btn--icon.btn--sm { width: var(--control-height-sm); height: var(--control-height-sm); }
.btn--icon.btn--lg { width: var(--control-height-lg); height: var(--control-height-lg); }
.btn--block { display: flex; width: 100%; }

/* Numeric counter that rides inside a button label ("Drafts 3"). */
.btn__count {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  opacity: 0.75;
  margin-left: var(--space-2xs);
}

/* A row of related buttons that should read as one control. */
.btn-group {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}


/* ==========================================================================
   08  CARDS
     .card         generic white module with a 1px border + whisper shadow
     .metric-card  the KPI tile: label / big figure / delta / footer / bar
   ========================================================================== */

.card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  min-width: 0;
}

/* Clickable card (a PO in the stream, a supplier tile). */
.card--interactive {
  cursor: pointer;
  transition: border-color var(--duration-base) var(--easing-standard),
              box-shadow var(--duration-base) var(--easing-standard);
}
.card--interactive:hover {
  border-color: var(--color-border-hover);
  box-shadow: var(--shadow-card-hover);
}
/* The currently inspected item. */
.card--selected {
  border-color: var(--color-primary);
  border-width: var(--border-width-thick);
  box-shadow: var(--shadow-card-hover);
}
/* Inset "workbench" panel — telemetry strips, nested forms. */
.card--sunken {
  background: var(--color-surface-sunken);
  box-shadow: none;
}
/* Left accent rail, for banners that need a category colour. */
.card--accent-primary   { border-left: 4px solid var(--color-primary); }
.card--accent-warning   { border-left: 4px solid var(--color-secondary); }
.card--accent-info      { border-left: 4px solid var(--color-tertiary); }
.card--accent-danger    { border-left: 4px solid var(--color-danger); }
/* Quiet card for completed / archived rows. */
.card--muted { box-shadow: var(--shadow-xs); background: var(--color-surface); opacity: 0.88; }
/* Shadowless card — for a card nested inside another card or panel, where a
   second drop shadow would read as noise. Keeps the border and the radius. */
.card--flush { box-shadow: none; }

.card__header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-base) var(--space-lg);
  border-bottom: var(--border-width) solid var(--color-border);
}
.card__header-text { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }
.card__title {
  font-family: var(--font-serif);
  font-size: var(--font-size-headline-md);
  line-height: var(--line-height-headline-md);
  letter-spacing: var(--letter-spacing-headline-md);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
}
.card__subtitle {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-secondary);
}
.card__actions { display: flex; align-items: center; gap: var(--space-xs); flex: 0 0 auto; }

.card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg);
  flex: 1 1 auto;
  min-width: 0;
}
.card__body--flush   { padding: 0; }
.card__body--tight   { padding: var(--space-md); }
.card__body--roomy   { padding: var(--space-xl); }

.card__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  border-top: var(--border-width) solid var(--color-border);
  background: var(--color-surface-hover);
}

/* --- .metric-card : the KPI tile -----------------------------------------
     <article class="metric-card">
       <div class="metric-card__top">
         <div class="metric-card__head">
           <span class="metric-card__label">Active Pipeline</span>
           <div class="metric-card__value-row">
             <span class="metric-card__value">6</span>
             <span class="metric-card__unit">In Progress</span>
           </div>
         </div>
         <span class="metric-card__icon metric-card__icon--primary">...</span>
       </div>
       <div class="metric-card__footer">
         <span class="metric-card__footer-label">Total Committed</span>
         <span class="metric-card__footer-value">$4,850.00</span>
       </div>
       <div class="progress progress--sm"><div class="progress__bar is-70"></div></div>
     </article>                                                              */

.metric-card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-lg);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  position: relative;
  overflow: hidden;
  min-width: 0;
  transition: border-color var(--duration-base) var(--easing-standard),
              box-shadow var(--duration-base) var(--easing-standard);
}
.metric-card--interactive:hover {
  border-color: var(--color-border-hover);
  box-shadow: var(--shadow-card-hover);
  cursor: pointer;
}

.metric-card__top  { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-md); }
.metric-card__head { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }

.metric-card__label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.metric-card__label--warning { color: var(--color-warning-text); }
.metric-card__label--info    { color: var(--color-info-text); }

.metric-card__value-row { display: flex; align-items: baseline; gap: var(--space-xs); flex-wrap: wrap; }

.metric-card__value {
  font-family: var(--font-sans);
  font-size: var(--font-size-metric-val);
  line-height: var(--line-height-metric-val);
  letter-spacing: var(--letter-spacing-metric-val);
  font-weight: var(--font-weight-metric-val);
  color: var(--color-text);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
}
.metric-card__value--primary { color: var(--color-primary-text); }
.metric-card__value--warning { color: var(--color-warning-text); }
.metric-card__value--info    { color: var(--color-info-text); }
.metric-card__value--danger  { color: var(--color-danger); }

/* The "In Progress" / "Days Lead Time" qualifier next to the figure. */
.metric-card__unit {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-muted);
}

/* Trend delta tag. Use --up / --down so the arrow and the colour agree. */
.metric-card__delta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text-muted);
}
.metric-card__delta--up   { color: var(--color-primary); }
.metric-card__delta--down { color: var(--color-danger); }
.metric-card__delta--flat { color: var(--color-text-muted); }

/* 40x40 tinted glyph box in the top-right corner. */
.metric-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
}
.metric-card__icon--primary { background: var(--color-primary-soft);   color: var(--color-primary); }
.metric-card__icon--warning { background: var(--color-secondary-soft); color: var(--color-secondary); }
.metric-card__icon--info    { background: var(--color-tertiary-soft);  color: var(--color-tertiary); }
.metric-card__icon--danger  { background: var(--color-danger-soft);    color: var(--color-danger); }

/* Bottom strip: one supporting label/value pair above a hairline. */
.metric-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: var(--border-width) solid var(--color-border-subtle);
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
}
.metric-card__footer-label { color: var(--color-text-muted); font-family: var(--font-sans); }
.metric-card__footer-value { color: var(--color-text); font-weight: var(--font-weight-semibold); }
.metric-card__footer-value--primary { color: var(--color-primary-text); }
.metric-card__footer-value--warning { color: var(--color-warning-text); }
.metric-card__footer-value--info    { color: var(--color-info-text); }

/* --- .metric-card--slim : one-line KPI, for a dashboard that must fit the screen.
     Same tile, laid out left to right: icon, label over figure, footer to the right of a
     rule instead of under one. About half the height of the standard tile. */
.metric-card--slim {
  flex-direction: row;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  /* A slim tile puts icon, label, figure and footer on one line, so it needs the width of
     a full-page strip. Put one in a half-page column and the parts stop fitting — and
     because the card clips its overflow, what you get is the figure printed over the icon
     rather than anything that looks wrong enough to notice. Wrapping instead means a tile
     that is too narrow goes to two lines and stays readable. */
  flex-wrap: wrap;
  row-gap: var(--space-2xs);
}
.metric-card--slim .metric-card__top   { flex: 1 1 auto; align-items: center; gap: var(--space-sm); min-width: 0; }
/* Wide enough for a figure, so a narrow tile wraps rather than letting one overflow the
   other. Without a floor here min-width: 0 lets the head shrink under its own content. */
.metric-card--slim .metric-card__head  { flex: 1 1 auto; min-width: 7rem; }
.metric-card--slim .metric-card__value {
  font-size: var(--font-size-headline-md);
  line-height: var(--line-height-headline-md);
  letter-spacing: var(--letter-spacing-headline-md);
}
.metric-card--slim .metric-card__icon { width: 2rem; height: 2rem; }
.metric-card--slim .metric-card__footer {
  flex: 0 0 auto;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
  margin: 0;
  padding: 0 0 0 var(--space-md);
  border-top: 0;
  border-left: var(--border-width) solid var(--color-border-subtle);
}


/* ==========================================================================
   09  CHIPS, BADGES & SMALL MARKERS
     .chip         24px status pill. Variants carry the meaning.
     .badge-count  small numeric counter (notification bubble)
     .tag-dot      2px status dot for "live / idle / fault"
     .kbd          keyboard hint
     .avatar       user / supplier initial square or circle
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  height: var(--chip-height);
  padding-inline: var(--space-sm);
  border-radius: var(--radius-md);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  font-family: var(--font-sans);
  font-size: var(--font-size-label-caps);
  line-height: 1;
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  white-space: nowrap;
}
/* Fully rounded. Reserved for live phase tags and connection status. */
.chip--pill { border-radius: var(--radius-pill); padding-inline: var(--space-md); }
.chip--sm   { height: 1.25rem; padding-inline: var(--space-xs); font-size: 10px; }
.chip--square { border-radius: var(--radius-xs); }

.chip--success { background: var(--color-primary-soft);   border-color: var(--color-primary-border);   color: var(--color-primary-text); }
.chip--warning { background: var(--color-secondary-soft);  border-color: var(--color-secondary-border); color: var(--color-secondary-text); }
.chip--neutral { background: var(--color-surface-sunken);  border-color: var(--color-border-strong);    color: var(--color-slate-600); }
.chip--danger  { background: var(--color-danger-soft);     border-color: var(--color-danger-border);    color: var(--color-danger-text); }
.chip--info    { background: var(--color-tertiary-soft);   border-color: var(--color-tertiary-border);  color: var(--color-tertiary-text); }
.chip--primary { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
.chip--inverse { background: var(--color-surface-inverse); border-color: var(--color-surface-inverse); color: var(--color-text-inverse); }

/* Numeric chip content keeps tabular figures ("SLA: 99.1%"). */
.chip__num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  letter-spacing: 0;
}
.chip__dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: var(--radius-pill);
  background: currentColor;
  flex: 0 0 auto;
}

/* --- .badge-count : notification bubble ---------------------------------- */
.badge-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.125rem;
  height: 1.125rem;
  padding-inline: 0.25rem;
  border-radius: var(--radius-pill);
  background: var(--color-secondary-strong);
  color: var(--color-text-inverse);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  box-shadow: var(--shadow-xs);
}
.badge-count--primary { background: var(--color-primary); }
.badge-count--danger  { background: var(--color-danger); }
.badge-count--neutral { background: var(--color-slate-400); }

/* Wrapper that anchors a .badge-count to the corner of a button. */
.badge-anchor { position: relative; display: inline-flex; }
.badge-anchor > .badge-count { position: absolute; top: -0.25rem; right: -0.25rem; }

/* --- .tag-dot : status LED ------------------------------------------------ */
.tag-dot {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--color-slate-400);
  flex: 0 0 auto;
}
.tag-dot--success { background: var(--color-primary-strong); }
.tag-dot--warning { background: var(--color-secondary-strong); }
.tag-dot--danger  { background: var(--color-danger); }
.tag-dot--info    { background: var(--color-tertiary-strong); }
.tag-dot--pulse   { animation: ds-pulse 1.8s var(--easing-standard) infinite; }

@keyframes ds-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* --- .kbd ----------------------------------------------------------------- */
.kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  padding: 0.125rem 0.375rem;
  border-radius: var(--radius-xs);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-xs);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
}

/* --- .avatar -------------------------------------------------------------- */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-circle);
  background: var(--color-surface-sunken);
  border: var(--border-width) solid var(--color-border);
  color: var(--color-text-secondary);
  font-size: var(--font-size-body-sm);
  font-weight: var(--font-weight-bold);
  overflow: hidden;
  object-fit: cover;
}
.avatar--square { border-radius: var(--radius-md); }
.avatar--lg     { width: 2.5rem; height: 2.5rem; font-size: var(--font-size-body-md); }
.avatar--xl     { width: 3.5rem; height: 3.5rem; border-radius: var(--radius-lg); font-size: var(--font-size-headline-lg); }
.avatar--primary { background: var(--color-primary-soft);   border-color: var(--color-primary-border);   color: var(--color-primary-text); }
.avatar--warning { background: var(--color-secondary-soft); border-color: var(--color-secondary-border); color: var(--color-secondary-text); }
.avatar--info    { background: var(--color-tertiary-soft);  border-color: var(--color-tertiary-border);  color: var(--color-tertiary-text); }

/* Name + role block next to an avatar in the topbar. */
.user-chip       { display: flex; align-items: center; gap: var(--space-sm); cursor: pointer; }
.user-chip__text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.user-chip__name { font-size: var(--font-size-title-sm); font-weight: var(--font-weight-semibold); color: var(--color-text); }
.user-chip__role { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }


/* ==========================================================================
   10  TABLES
     .table-wrap          rounded, bordered, horizontally scrollable shell
     .data-table          the table itself — 48px rows, sunken caps header,
                          1px dividers, ivory hover
     .data-table--numeric every cell gets tabular figures
     .data-table--compact 40px rows for dense log views
   Cell helpers: .data-table__num  .data-table__actions
                 .cell-title  .cell-sub
   ========================================================================== */

.table-wrap {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  overflow: auto;
  min-width: 0;
  box-shadow: var(--shadow-card);
}
.table-wrap--flush { box-shadow: none; }

.data-table {
  width: 100%;
  text-align: left;
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-secondary);
}

.data-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  text-align: left;
  padding: var(--space-md) var(--space-md);
  border-bottom: var(--border-width) solid var(--color-border);
  white-space: nowrap;
}

.data-table tbody tr {
  border-top: var(--border-width) solid var(--color-border-subtle);
  transition: background-color var(--duration-fast) var(--easing-standard);
}
.data-table tbody tr:first-child { border-top: 0; }
.data-table tbody tr:hover       { background: var(--color-surface-hover); }
.data-table tbody tr.is-selected {
  background: var(--color-primary-soft);
  box-shadow: inset 3px 0 0 0 var(--color-primary);
}
.data-table tbody tr.is-muted td { color: var(--color-text-subtle); }

.data-table tbody td {
  height: var(--table-row-height);
  padding: var(--space-sm) var(--space-md);
  vertical-align: middle;
  color: var(--color-text-secondary);
}

.data-table tfoot td {
  background: var(--color-surface-sunken);
  border-top: var(--border-width) solid var(--color-border);
  padding: var(--space-md);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

/* All-numeric table (roast logs, ledgers). */
.data-table--numeric tbody td,
.data-table--numeric tfoot td {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-md);
  font-weight: var(--font-weight-numeric-md);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
}
.data-table--compact tbody td { height: 2.5rem; padding-block: var(--space-xs); }
/* Every column on one line — the wrapper scrolls sideways instead of rows growing taller.
   For drill-downs, where the columns are many and each value is short. */
.data-table--nowrap thead th,
.data-table--nowrap tbody td { white-space: nowrap; }

/* Vertical rules between columns, for wide comparison grids. */
.data-table--ruled tbody td + td,
.data-table--ruled thead th + th { border-left: var(--border-width) solid var(--color-border-subtle); }

/* Right-aligned tabular cell. Put this on money / qty / temp columns. */
.data-table__num,
.data-table th.data-table__num {
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-md);
  font-weight: var(--font-weight-numeric-md);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
  white-space: nowrap;
}
.data-table__num--strong { color: var(--color-text); font-weight: var(--font-weight-bold); }
/* Trailing cell holding row buttons; keeps them from stretching the row. */
.data-table__actions { width: 1%; white-space: nowrap; text-align: right; }
.data-table__actions > * + * { margin-left: var(--space-xs); }
/* Fixed narrow leading cell for checkboxes. */
.data-table__select { width: 2.75rem; }

/* Two-line cell: bold name over a grey qualifier. */
.cell-stack { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
/* A face and a name in one cell. Not .cluster, which wraps — and a wrapped avatar drops
   onto its own line and doubles the height of every row in the table. */
.cell-person { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--space-sm); min-width: 0; }

/* ---- Offer letter ----
   The body is edited as plain text with the braces visible, so it is set in the mono face:
   {{employee_name}} in a proportional font looks like a mistake rather than a field. */
.offer-body { font-family: var(--font-mono); font-size: var(--font-size-body-sm); line-height: 1.7; }
/* The preview is the real A4 page, scaled to the column by the script. Its own background
   is white because that is what paper is, whatever the app around it is painted. */
.offer-preview {
  width: 100%; border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md); background: #fff; display: block;
}
/* A signature is a picture of ink; it sits on the page, not in a box. */
.offer-signature {
  max-width: 14rem; max-height: 5rem; display: block;
  padding: var(--space-sm); background: #fff;
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius-md);
}
.cell-person > .avatar { flex: 0 0 auto; }
.cell-person > .cell-stack { min-width: 0; }
.cell-title {
  font-size: var(--font-size-title-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.cell-sub {
  font-size: 11px;
  line-height: 1.25;
  color: var(--color-text-muted);
}
/* A long free-text cell (an order's item list): clamped so one chatty row cannot push a
   whole table sideways. The full text stays in the cell's title. */
.cell-wide {
  display: inline-block;
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* Monospaced identifier cell (SKU, lot, batch). */
.cell-code {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text-muted);
}

/* Bar above a table holding a title, a count and filters. */
.table-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-bottom: var(--space-sm);
}


/* ==========================================================================
   11  FORMS
     .form-field   label + control + hint, stacked
     .form-label   uppercase caps label (add .form-label__required for the *)
     .form-input / .form-select / .form-textarea   40px controls
     .form-check   checkbox or radio + label
     .form-switch  on/off toggle
     .input-group  control with leading/trailing addons or stepper buttons
     .form-hint / .form-error
   Focus for every control = olive border + 2px olive ring + a warm tint.
   ========================================================================== */

.form-field { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }
.form-field--inline { flex-direction: row; align-items: center; gap: var(--space-md); }

.form-label {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}
.form-label--sentence {
  text-transform: none;
  font-size: var(--font-size-label-md);
  letter-spacing: var(--letter-spacing-label-md);
  font-weight: var(--font-weight-semibold);
}
.form-label__required { color: var(--color-danger); }
.form-label__optional { color: var(--color-text-subtle); font-weight: var(--font-weight-medium); }

/* --- Text-ish controls ---------------------------------------------------- */
.form-input,
.form-select,
.form-textarea {
  width: 100%;
  min-width: 0;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  box-shadow: var(--shadow-xs);
  transition: var(--transition-colors);
}

.form-input,
.form-select {
  height: var(--control-height);
  padding-inline: var(--space-md);
}

.form-textarea {
  min-height: 5rem;
  padding: var(--space-md);
  resize: vertical;
}

/* Native select needs its arrow drawn by us. The chevron below is an inline
   SVG data URI — no network request. */
.form-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.25rem;
  cursor: pointer;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-md) center;
  background-size: 16px 16px;
}

.form-input::placeholder,
.form-textarea::placeholder { color: var(--color-text-subtle); }

.form-input:hover,
.form-select:hover,
.form-textarea:hover { border-color: var(--color-slate-400); }

.form-input:focus,
.form-select:focus,
.form-textarea:focus,
.form-input:focus-visible,
.form-select:focus-visible,
.form-textarea:focus-visible {
  outline: none;
  border-color: var(--color-focus);
  box-shadow: var(--shadow-focus);
  background-color: var(--color-focus-tint);
}

.form-input:disabled,
.form-select:disabled,
.form-textarea:disabled,
.form-input[readonly] {
  background: var(--color-surface-sunken);
  color: var(--color-text-subtle);
  cursor: not-allowed;
  box-shadow: none;
}

/* Numeric input variant — mono + tabular + right aligned. */
.form-input--numeric {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
  text-align: right;
}
.form-input--center { text-align: center; }
.form-input--sm { height: var(--control-height-sm); font-size: var(--font-size-body-sm); padding-inline: var(--space-sm); }
.form-select--sm { height: var(--control-height-sm); font-size: var(--font-size-body-sm); }
/* A select inside a grid cell that has to stay readable — the shift boxes on a rota, where
   "Morning" cut to "Mo" tells nobody anything. The table scrolls sideways instead. */
.form-select--grid { min-width: 7rem; }

/* Invalid state. Put .is-invalid on the control and render a .form-error. */
.form-input.is-invalid,
.form-select.is-invalid,
.form-textarea.is-invalid { border-color: var(--color-danger-border); background-color: var(--color-danger-soft); }
.form-input.is-invalid:focus,
.form-select.is-invalid:focus,
.form-textarea.is-invalid:focus {
  border-color: var(--color-danger);
  box-shadow: 0 0 0 2px rgba(185, 74, 72, 0.22);
  background-color: var(--color-danger-soft);
}

/* --- Field with an icon sitting inside the control ------------------------ */
.form-field--with-icon { position: relative; }
.form-field--with-icon .form-input { padding-left: 2.5rem; }
.form-field__icon {
  position: absolute;
  left: var(--space-md);
  bottom: 0.625rem;
  color: var(--color-text-subtle);
  pointer-events: none;
  font-size: 18px;
}
/* Same thing but for a standalone search box (no label above). */
.search-field { position: relative; display: flex; align-items: center; width: 100%; }
.search-field .form-input { padding-left: 2.5rem; padding-right: 3rem; background: var(--color-surface-sunken); }
.search-field .form-input:focus { background: var(--color-surface); }
.search-field__icon { position: absolute; left: var(--space-md); color: var(--color-text-subtle); pointer-events: none; font-size: 18px; }
.search-field__hint { position: absolute; right: var(--space-sm); }

/* --- .input-group : addons and steppers -----------------------------------
     <div class="input-group">
       <button class="input-group__btn">-</button>
       <input class="input-group__input form-input--numeric form-input--center">
       <button class="input-group__btn">+</button>
       <span class="input-group__addon">60kg ea</span>
     </div>                                                                  */
.input-group {
  display: flex;
  align-items: stretch;
  width: 100%;
  min-width: 0;
  height: var(--control-height);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
  transition: var(--transition-colors);
}
.input-group:focus-within {
  border-color: var(--color-focus);
  box-shadow: var(--shadow-focus);
}
.input-group__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  padding-inline: var(--space-md);
  color: var(--color-text);
  font-size: var(--font-size-body-md);
  font-weight: var(--font-weight-semibold);
}
.input-group__input:focus { outline: none; }
.input-group__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2.25rem;
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  border: 0;
  cursor: pointer;
  transition: var(--transition-colors);
}
.input-group__btn:hover { background: var(--color-border); color: var(--color-text); }
/* Static unit / currency label at either end of the group. */
.input-group__addon {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding-inline: var(--space-sm);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  font-size: var(--font-size-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  white-space: nowrap;
  border-left: var(--border-width) solid var(--color-border);
}
.input-group__addon--leading { border-left: 0; border-right: var(--border-width) solid var(--color-border); }
.input-group__addon--primary { background: var(--color-primary-soft); color: var(--color-primary-text); }
.input-group__addon--info    { background: var(--color-tertiary-soft); color: var(--color-tertiary-text); }

/* Read-only computed output styled like a control (e.g. line subtotal). */
.form-output {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  height: var(--control-height);
  padding-inline: var(--space-md);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
}
.form-output__label { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }
.form-output__value {
  font-family: var(--font-mono);
  font-size: var(--font-size-headline-sm);
  font-weight: var(--font-weight-bold);
  color: var(--color-primary-text);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
}

/* --- .form-check : checkbox / radio --------------------------------------
     <label class="form-check">
       <input type="checkbox" class="form-check__input">
       <span class="form-check__text">
         <span class="form-check__label">Cold-Chain Verification</span>
         <span class="form-check__hint">Mandatory dock temp log</span>
       </span>
     </label>                                                                */
.form-check {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  cursor: pointer;
  min-width: 0;
}
/* Boxed variant — the whole row is a clickable tile. */
.form-check--boxed {
  padding: var(--space-sm) var(--space-md);
  background: var(--color-surface-hover);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  transition: var(--transition-colors);
}
.form-check--boxed:hover { background: var(--color-surface-sunken); }

.form-check__input {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 1.125rem;   /* 18px */
  height: 1.125rem;
  margin: 0;
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-xs);
  background: var(--color-surface);
  cursor: pointer;
  display: inline-block;
  position: relative;
  transition: var(--transition-colors);
}
.form-check__input[type="radio"] { border-radius: var(--radius-pill); }
.form-check__input:hover { border-color: var(--color-primary); }
.form-check__input:checked {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  /* White check mark, inline SVG data URI — offline safe. */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  background-size: 0.75rem 0.75rem;
  background-position: center;
  background-repeat: no-repeat;
}
.form-check__input[type="radio"]:checked {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='6' fill='%23ffffff'/%3E%3C/svg%3E");
  background-size: 0.625rem 0.625rem;
}
.form-check__input:indeterminate {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='%23ffffff' stroke-width='3.5' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
  background-size: 0.75rem 0.75rem;
  background-position: center;
  background-repeat: no-repeat;
}
.form-check__input:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.form-check__input:disabled { background: var(--color-surface-sunken); border-color: var(--color-border); cursor: not-allowed; }

.form-check__text  { display: flex; flex-direction: column; min-width: 0; }
.form-check__label { font-size: var(--font-size-title-sm); font-weight: var(--font-weight-semibold); color: var(--color-text); }
.form-check__hint  { font-size: 11px; line-height: 1.3; color: var(--color-text-muted); }

/* --- .form-switch : on/off toggle ----------------------------------------
     <label class="form-switch">
       <input type="checkbox" class="form-switch__input">
       <span class="form-switch__track"></span>
       <span class="form-switch__label">Auto-reorder</span>
     </label>                                                                */
.form-switch { display: inline-flex; align-items: center; gap: var(--space-sm); cursor: pointer; }
.form-switch__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.form-switch__track {
  position: relative;
  flex: 0 0 auto;
  width: 2.25rem;   /* 36px */
  height: 1.25rem;  /* 20px */
  border-radius: var(--radius-pill);
  background: var(--color-border-strong);
  transition: background-color var(--duration-base) var(--easing-standard),
              box-shadow var(--duration-fast) var(--easing-standard);
}
.form-switch__track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 1rem;
  height: 1rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  transition: transform var(--duration-base) var(--easing-emphasis);
}
.form-switch__input:checked + .form-switch__track { background: var(--color-primary); }
.form-switch__input:checked + .form-switch__track::after { transform: translateX(1rem); }
.form-switch__input:focus-visible + .form-switch__track { box-shadow: var(--shadow-focus); }
.form-switch__input:disabled + .form-switch__track { opacity: 0.5; cursor: not-allowed; }
.form-switch__label { font-size: var(--font-size-body-md); color: var(--color-text-secondary); }

/* --- Helper text ---------------------------------------------------------- */
.form-hint {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-muted);
}
.form-error {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-danger);
}

/* --- Form layout helpers -------------------------------------------------- */
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-md);
}
.form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.form-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.form-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-top: var(--space-md);
  border-top: var(--border-width) solid var(--color-border);
}
/* A grouped block of related fields on a sunken background. */
.form-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--color-surface-sunken);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
}
.form-section__title {
  font-size: var(--font-size-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}
.fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }


/* ==========================================================================
   12  FEEDBACK
     .alert        inline banner attached to a region
     .toast        transient message, stacked in .toast-stack
     .empty-state  "nothing here yet"
     .spinner      indeterminate loading
     .progress     determinate bar (width via .is-N step classes)
     .skeleton     shimmering placeholder
   ========================================================================== */

/* --- .alert --------------------------------------------------------------- */
.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-base);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
}
.alert__icon    { flex: 0 0 auto; margin-top: 1px; color: currentColor; }
.alert__content { display: flex; flex-direction: column; gap: var(--space-2xs); flex: 1 1 auto; min-width: 0; }
.alert__title   { font-size: var(--font-size-title-sm); font-weight: var(--font-weight-bold); color: var(--color-text); }
.alert__body    { font-size: var(--font-size-body-sm); line-height: var(--line-height-body-sm); }
.alert__actions { display: flex; align-items: center; gap: var(--space-sm); flex: 0 0 auto; }

.alert--success { background: var(--color-primary-soft);   border-color: var(--color-primary-border);   color: var(--color-primary-text); }
.alert--warning { background: var(--color-secondary-soft);  border-color: var(--color-secondary-border); color: var(--color-secondary-text); }
.alert--danger  { background: var(--color-danger-soft);     border-color: var(--color-danger-border);    color: var(--color-danger-text); }
.alert--info    { background: var(--color-tertiary-soft);   border-color: var(--color-tertiary-border);  color: var(--color-tertiary-text); }
.alert--neutral { background: var(--color-surface-sunken);  border-color: var(--color-border);           color: var(--color-text-secondary); }
.alert--success .alert__title,
.alert--warning .alert__title,
.alert--danger  .alert__title,
.alert--info    .alert__title { color: inherit; }

/* --- .toast --------------------------------------------------------------- */
.toast-stack {
  position: fixed;
  right: var(--space-lg);
  bottom: var(--space-lg);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: min(24rem, calc(100vw - var(--space-xl)));
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-base);
  border-radius: var(--radius-md);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-surface);
  box-shadow: var(--shadow-overlay);
  color: var(--color-text-secondary);
  font-size: var(--font-size-body-md);
  pointer-events: auto;
  animation: ds-toast-in var(--duration-slow) var(--easing-emphasis);
}
.toast__icon    { flex: 0 0 auto; margin-top: 1px; }
.toast__content { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-width: 0; }
.toast__title   { font-size: var(--font-size-title-sm); font-weight: var(--font-weight-bold); color: var(--color-text); }
.toast__body    { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }
.toast__close   { flex: 0 0 auto; color: var(--color-text-subtle); cursor: pointer; }
.toast__close:hover { color: var(--color-text); }

.toast--success { border-left: 3px solid var(--color-primary); }
.toast--warning { border-left: 3px solid var(--color-secondary); }
.toast--danger  { border-left: 3px solid var(--color-danger); }
.toast--info    { border-left: 3px solid var(--color-tertiary); }

@keyframes ds-toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* --- .empty-state --------------------------------------------------------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-2xl) var(--space-lg);
  text-align: center;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text-muted);
}
.empty-state--flush { border: 0; background: transparent; }
.empty-state__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-lg);
  background: var(--color-surface-sunken);
  color: var(--color-text-subtle);
  margin-bottom: var(--space-xs);
}
.empty-state__title {
  font-size: var(--font-size-headline-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.empty-state__text { font-size: var(--font-size-body-sm); color: var(--color-text-muted); max-width: 28rem; }
.empty-state__action { margin-top: var(--space-sm); }

/* --- .spinner ------------------------------------------------------------- */
.spinner {
  display: inline-block;
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-primary);
  border-radius: var(--radius-pill);
  animation: ds-spin 0.7s linear infinite;
  flex: 0 0 auto;
}
.spinner--sm { width: 0.875rem; height: 0.875rem; border-width: 2px; }
.spinner--lg { width: 2rem; height: 2rem; border-width: 3px; }
.spinner--inverse { border-color: rgba(255, 255, 255, 0.35); border-top-color: #ffffff; }

@keyframes ds-spin { to { transform: rotate(360deg); } }

/* Full-panel loading cover. */
.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  border-radius: inherit;
  z-index: var(--z-sticky);
  font-size: var(--font-size-body-sm);
  color: var(--color-text-muted);
}

/* --- .progress ------------------------------------------------------------
   No inline styles allowed, so the fill width comes from a step class:
   .is-0 .is-5 .is-10 ... .is-100 (5% steps).
     <div class="progress"><div class="progress__bar is-70"></div></div>     */
.progress {
  width: 100%;
  height: 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface-sunken);
  overflow: hidden;
}
.progress--sm { height: 0.375rem; }
.progress--lg { height: 0.75rem; }
.progress__bar {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--color-primary-strong);
  transition: width var(--duration-slow) var(--easing-emphasis);
}
.progress__bar--warning { background: var(--color-secondary-strong); }
.progress__bar--danger  { background: var(--color-danger); }
.progress__bar--info    { background: var(--color-tertiary-strong); }
.progress__bar--neutral { background: var(--color-slate-400); }

.progress__bar.is-0   { width: 0%;   }
.progress__bar.is-5   { width: 5%;   }
.progress__bar.is-10  { width: 10%;  }
.progress__bar.is-15  { width: 15%;  }
.progress__bar.is-20  { width: 20%;  }
.progress__bar.is-25  { width: 25%;  }
.progress__bar.is-30  { width: 30%;  }
.progress__bar.is-35  { width: 35%;  }
.progress__bar.is-40  { width: 40%;  }
.progress__bar.is-45  { width: 45%;  }
.progress__bar.is-50  { width: 50%;  }
.progress__bar.is-55  { width: 55%;  }
.progress__bar.is-60  { width: 60%;  }
.progress__bar.is-65  { width: 65%;  }
.progress__bar.is-70  { width: 70%;  }
.progress__bar.is-75  { width: 75%;  }
.progress__bar.is-80  { width: 80%;  }
.progress__bar.is-85  { width: 85%;  }
.progress__bar.is-90  { width: 90%;  }
.progress__bar.is-95  { width: 95%;  }
.progress__bar.is-100 { width: 100%; }

/* Label row that sits above a progress bar. */
.progress-row { display: flex; flex-direction: column; gap: var(--space-xs); width: 100%; }
.progress-row__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  font-size: var(--font-size-body-sm);
  color: var(--color-text-muted);
}

/* --- .skeleton ------------------------------------------------------------ */
.skeleton {
  display: block;
  border-radius: var(--radius-sm);
  background-color: var(--color-surface-sunken);
  background-image: linear-gradient(
    90deg,
    rgba(232, 226, 213, 0) 0%,
    rgba(232, 226, 213, 0.85) 50%,
    rgba(232, 226, 213, 0) 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: ds-shimmer 1.4s linear infinite;
}
.skeleton--text   { height: 0.75rem; margin-block: 0.25rem; }
.skeleton--title  { height: 1.25rem; width: 40%; }
.skeleton--line   { height: 0.75rem; width: 100%; }
.skeleton--short  { width: 60%; }
.skeleton--block  { height: 6rem; border-radius: var(--radius-lg); }
.skeleton--circle { width: 2.5rem; height: 2.5rem; border-radius: var(--radius-pill); }
.skeleton--row    { height: var(--table-row-height); border-radius: var(--radius-sm); }

@keyframes ds-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}


/* ==========================================================================
   13  OVERLAYS / MODAL
     <div class="modal is-open">
       <div class="modal__backdrop"></div>
       <div class="modal__dialog" role="dialog" aria-modal="true">
         <header class="modal__header"> ... </header>
         <div class="modal__body"> ... </div>
         <footer class="modal__footer"> ... </footer>
       </div>
     </div>
   .modal is display:none until .is-open is added. Toggling that class is the
   only job the app's JS has here — no styling from script.
   ========================================================================== */

.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
}
.modal.is-open { display: flex; }

.modal__backdrop {
  position: absolute;
  inset: 0;
  background: var(--color-backdrop);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: var(--z-backdrop);
  animation: ds-fade-in var(--duration-base) var(--easing-standard);
}

.modal__dialog {
  position: relative;
  z-index: calc(var(--z-backdrop) + 1);
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 34rem;
  max-height: calc(100vh - var(--space-2xl));
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-overlay);
  overflow: hidden;
  animation: ds-dialog-in var(--duration-slow) var(--easing-emphasis);
}
/* --- .value-link : a figure that opens the rows behind it ------------------
     Put it on any number the user can click to drill into. It keeps the text it sits in
     (size, weight, alignment) and only marks itself as openable. */
.value-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: inherit;
  cursor: pointer;
  border-bottom: 1px dotted var(--color-border-strong);
  transition: var(--transition-colors);
}
.value-link:hover       { color: var(--color-primary-text); border-bottom-color: var(--color-primary); }
.value-link:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 2px; }
.value-link--muted      { color: var(--color-text-muted); }
/* In a matrix cell the dotted rule belongs under the figure, not the whole stack. The width
   matters too: a <button> shrinks to its content, which would leave clickable figures short
   of the column's right edge while the plain ones sat against it. */
.matrix-cell.value-link { border-bottom: 0; width: 100%; }
.matrix-cell.value-link .matrix-cell__amount { border-bottom: 1px dotted var(--color-border-strong); }
.matrix-cell.value-link:hover .matrix-cell__amount { color: var(--color-primary-text); border-bottom-color: var(--color-primary); }

.modal__dialog--sm { max-width: 24rem; }
.modal__dialog--lg { max-width: 48rem; }
.modal__dialog--xl { max-width: 64rem; }
/* Drill-downs: as wide as the window allows, because they carry every field that explains
   the figure that was clicked. */
.modal__dialog--full { max-width: calc(100vw - 4rem); }

.modal__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-base) var(--space-lg);
  border-bottom: var(--border-width) solid var(--color-border);
  flex: 0 0 auto;
}
.modal__header-text { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }
.modal__title {
  font-family: var(--font-serif);
  font-size: var(--font-size-headline-md);
  line-height: var(--line-height-headline-md);
  letter-spacing: var(--letter-spacing-headline-md);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
}
.modal__subtitle { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }
.modal__close {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: var(--transition-colors);
}
.modal__close:hover { background: var(--color-surface-sunken); color: var(--color-text); }

.modal__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg);
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.modal__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  border-top: var(--border-width) solid var(--color-border);
  background: var(--color-surface-hover);
  flex: 0 0 auto;
}
.modal__footer--split { justify-content: space-between; }
/* A dialog that keeps its actions beside its heading has nothing left down here, and an
   empty bar is just height the content could have had. */
.modal__footer:empty { display: none; }

@keyframes ds-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ds-dialog-in {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}


/* ==========================================================================
   14  NAVIGATION BITS
     .segmented  inset track with a sliding white active pill
     .tabs       underlined tab strip
     .steps      horizontal journey / milestone tracker
     .pagination page controls under a table
   ========================================================================== */

/* --- .segmented ----------------------------------------------------------- */
.segmented {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0.25rem;
  background: var(--color-surface-sunken);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
}
/* White-track variant used as a filter bar above a table. */
.segmented--onSurface { background: var(--color-surface); box-shadow: var(--shadow-xs); }
.segmented--block     { display: flex; width: 100%; }
.segmented--block .segmented__option { flex: 1 1 0; justify-content: center; }

.segmented__option {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-title-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-muted);
  white-space: nowrap;
  cursor: pointer;
  transition: var(--transition-colors);
}
.segmented__option:hover { color: var(--color-text); background: var(--color-surface-hover); }
.segmented__option.is-active {
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-card);
}
/* --- .segmented--view : the switch between two faces of one screen ---------
     Bigger than a filter chip and carrying an icon, because which face you are looking at
     has to be obvious from across the room, not inferred from the content. */
.segmented--view {
  border-color: var(--color-border-strong);
  box-shadow: var(--shadow-xs);
  gap: var(--space-2xs);
}
.segmented--view .segmented__option {
  padding: var(--space-xs) var(--space-base);
  font-size: var(--font-size-headline-sm);
  line-height: var(--line-height-headline-sm);
}
.segmented--view .segmented__option .material-symbols-outlined { font-size: 18px; }

/* Dark active pill (matches the reference's "All POs" filter). */
.segmented--inverse .segmented__option.is-active {
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse);
}
.segmented__count {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  opacity: 0.7;
}

/* --- .tabs ---------------------------------------------------------------- */
.tabs {
  display: flex;
  align-items: stretch;
  gap: var(--space-lg);
  border-bottom: var(--border-width) solid var(--color-border);
  overflow-x: auto;
}
.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-md) 0;
  border-bottom: var(--border-width-thick) solid transparent;
  margin-bottom: -1px;
  font-size: var(--font-size-title-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-muted);
  white-space: nowrap;
  cursor: pointer;
  transition: var(--transition-colors);
}
.tab:hover { color: var(--color-text); border-bottom-color: var(--color-border-strong); }
.tab.is-active { color: var(--color-primary-text); border-bottom-color: var(--color-primary); }
.tab.is-disabled { opacity: 0.45; pointer-events: none; }
.tab__count {
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text-subtle);
}

/* --- .steps : milestone tracker ------------------------------------------ */
.steps {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
  width: 100%;
  position: relative;
}
.step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  position: relative;
}
/* Connector line drawn from each step to the next. */
.step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 0.875rem;
  left: calc(50% + 1.25rem);
  right: calc(-50% + 1.25rem);
  height: 2px;
  background: var(--color-border);
}
.step.is-complete:not(:last-child)::after { background: var(--color-primary-strong); }
.step__marker {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  font-size: var(--font-size-body-sm);
  font-weight: var(--font-weight-bold);
  flex: 0 0 auto;
  position: relative;
  z-index: 1;
}
.step.is-complete .step__marker { background: var(--color-primary); color: var(--color-on-primary); box-shadow: var(--shadow-card); }
.step.is-current  .step__marker {
  background: var(--color-tertiary);
  color: var(--color-on-tertiary);
  box-shadow: 0 0 0 4px var(--color-tertiary-soft-alt);
}
.step.is-upcoming { opacity: 0.55; }
.step__text  { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.step__label { font-size: var(--font-size-body-sm); font-weight: var(--font-weight-semibold); color: var(--color-text); }
.step__meta  {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
}
.step.is-current .step__label,
.step.is-current .step__meta { color: var(--color-tertiary-text); }

/* --- .pagination ---------------------------------------------------------- */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-top: var(--space-md);
}
.pagination__info {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
}
.pagination__controls { display: flex; align-items: center; gap: var(--space-xs); }


/* ==========================================================================
   15  DATA DISPLAY
     .stat-row        one label / value line inside a card or summary panel
     .definition-list label-above-value meta grid (supplier contact strip)
     .summary-panel   the totals box at the end of an invoice
   ========================================================================== */

.stat-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-2xs);
  min-width: 0;
}
.stat-row--bordered { padding-block: var(--space-sm); border-top: var(--border-width) solid var(--color-border-subtle); }
.stat-row__label {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.stat-row__value {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
  white-space: nowrap;
}
.stat-row__value--text    { font-family: var(--font-sans); font-size: var(--font-size-body-sm); }
.stat-row__value--primary { color: var(--color-primary-text); }
.stat-row__value--warning { color: var(--color-warning-text); }
.stat-row__value--info    { color: var(--color-info-text); }
.stat-row__value--danger  { color: var(--color-danger); }
.stat-row--total {
  padding-top: var(--space-sm);
  border-top: var(--border-width) solid var(--color-border);
}
.stat-row--total .stat-row__label { font-size: var(--font-size-headline-sm); font-weight: var(--font-weight-bold); color: var(--color-text); }
.stat-row--total .stat-row__value { font-size: var(--font-size-headline-sm); font-weight: var(--font-weight-bold); color: var(--color-primary-text); }

/* --- .definition-list ----------------------------------------------------- */
.definition-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-md);
  padding: var(--space-md);
  background: var(--color-surface-hover);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
}
.definition-list__item  { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }
.definition-list__label {
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.definition-list__value { font-size: var(--font-size-title-sm); font-weight: var(--font-weight-semibold); color: var(--color-text); }
.definition-list__meta  { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }

/* --- .summary-panel ------------------------------------------------------- */
.summary-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  width: 100%;
  max-width: 18rem;
  margin-left: auto;
  padding: var(--space-md);
  background: var(--color-surface-hover);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
}


/* ==========================================================================
   16  UTILITY ESCAPE HATCHES
   Deliberately tiny. These exist for accessibility and text overflow only —
   they are NOT an invitation to build layouts out of utility classes.
   ========================================================================== */

.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;
}
.truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clamp-2  { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.is-hidden { display: none !important; }
.grow      { flex: 1 1 auto; min-width: 0; }
.no-shrink { flex: 0 0 auto; }


/* ==========================================================================
   17  RESPONSIVE BREAKPOINTS
     Desktop  >= 1024px : 12 columns, 24px gutters, 32px page margins
     Tablet   768-1023px:  8 columns, 20px gutters, 24px page margins
     Mobile   <  768px  :  4 columns, 16px gutters, 16px page margins
   Column spans clamp to the available count; below 768px everything in a
   .layout-grid goes full width.
   ========================================================================== */

/* ---- Desktop (>= 1024px) ------------------------------------------------- */
@media (min-width: 1024px) {
  :root {
    --grid-columns: 12;
    --grid-gutter:  var(--gutter-desktop);
    --page-gutter:  var(--page-margin-desktop);
  }
}

/* ---- Tablet (768px - 1023px) --------------------------------------------- */
@media (min-width: 768px) and (max-width: 1023.98px) {
  :root {
    --grid-columns: 8;
    --grid-gutter:  var(--gutter-tablet);
    --page-gutter:  var(--page-margin-tablet);
  }

  /* Spans beyond 8 collapse to full width. */
  .col-span-9,
  .col-span-10,
  .col-span-11,
  .col-span-12 { grid-column: 1 / -1; }
  /* The 5/7 split of the reference stacks into two full-width rows. */
  .col-span-5,
  .col-span-6,
  .col-span-7 { grid-column: 1 / -1; }

  /* Sidebar becomes an icon rail — labels and the meta strip hide. */
  .app-shell { grid-template-columns: var(--sidebar-rail-width) minmax(0, 1fr); }
  .app-sidebar { align-items: center; }
  .app-sidebar__brand { justify-content: center; padding-inline: var(--space-sm); }
  .app-sidebar__brand-text,
  .app-sidebar__section-label,
  .app-sidebar__meta,
  .nav-item__label,
  .nav-item__badge { display: none; }
  .nav-item { justify-content: center; padding-inline: var(--space-sm); border-right-width: 0; }

  .kpi-grid,
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .app-topbar__center { max-width: 18rem; }
}

/* ---- Mobile (< 768px) ---------------------------------------------------- */
@media (max-width: 767.98px) {
  :root {
    --grid-columns: 4;
    --grid-gutter:  var(--gutter);
    --page-gutter:  var(--page-margin);
  }

  /* Everything in the 12-col grid goes full width. */
  .col-span-1, .col-span-2,  .col-span-3,  .col-span-4,
  .col-span-5, .col-span-6,  .col-span-7,  .col-span-8,
  .col-span-9, .col-span-10, .col-span-11, .col-span-12 { grid-column: 1 / -1; }

  /* The sidebar is a drawer here, not a rail — see PHONES further down. A 3.5rem rail was
     the old treatment: it cost 56px of a 375px screen permanently and still showed nothing
     but icons, so the navigation was both always present and never readable. */

  /* Page title drops to the mobile size from the type scale. */
  .page-title {
    font-size: var(--font-size-headline-xl-mobile);
    line-height: var(--line-height-headline-xl-mobile);
    letter-spacing: var(--letter-spacing-headline-xl-mobile);
  }
  .section-title { font-size: var(--font-size-headline-md); line-height: var(--line-height-headline-md); }

  .kpi-grid, .kpi-grid--3, .kpi-grid--2,
  .grid-2, .grid-3, .grid-4,
  .form-grid, .form-grid--2, .form-grid--3, .form-grid--4 { grid-template-columns: minmax(0, 1fr); }

  .page-header { flex-direction: column; align-items: stretch; }
  .page-header__actions { width: 100%; }
  .page-header__actions .btn { flex: 1 1 auto; }

  /* Topbar keeps only what fits: context switch, primary action, avatar. */
  .app-topbar { padding-inline: var(--space-md); gap: var(--space-sm); }
  .app-topbar__center { display: none; }
  .app-topbar__start .status-pill { display: none; }
  .app-topbar__start { gap: var(--space-sm); }
  .context-switch__label { display: none; }
  .context-switch__value { max-width: 9rem; overflow: hidden; }
  .context-switch__value,
  .app-sidebar__brand-name { white-space: nowrap; text-overflow: ellipsis; }
  .user-chip__text { display: none; }
  .app-content { padding: var(--space-md) var(--page-gutter) var(--space-xl); gap: var(--space-md); }

  /* Telemetry / KPI strips become a horizontal snap carousel. */
  .kpi-grid--carousel {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: var(--space-md);
    padding-bottom: var(--space-xs);
  }
  .kpi-grid--carousel > * {
    flex: 0 0 78%;
    scroll-snap-align: start;
  }

  /* Milestone tracker stacks vertically. */
  .steps { flex-direction: column; align-items: stretch; gap: var(--space-md); }
  .step  { flex-direction: row; align-items: center; text-align: left; gap: var(--space-md); }
  .step:not(:last-child)::after { display: none; }
  .step__text { align-items: flex-start; }

  .modal { padding: var(--space-md); align-items: flex-end; }
  .modal__dialog { max-width: none; max-height: 88vh; }
  .toast-stack { left: var(--space-md); right: var(--space-md); bottom: var(--space-md); width: auto; }
  .summary-panel { max-width: none; }
}

/* ---- Print (packing slips, PO copies) ------------------------------------ */
@media print {
  .app-sidebar, .app-topbar, .toast-stack, .page-header__actions { display: none !important; }
  .app-shell { grid-template-columns: minmax(0, 1fr); }
  .card, .metric-card, .table-wrap { box-shadow: none; break-inside: avoid; }
  body { background: #ffffff; }
}

/* ==========================================================================
   APPENDED — INGREDIENTS / RECIPES MIGRATION
   --------------------------------------------------------------------------
   ingredients.js and recipes.js build their table rows, the recipe picker list
   and the Excel-import banner with innerHTML / className, using the Bootstrap
   names that predate this stylesheet. Those files carry page behaviour and were
   deliberately left untouched by a visual migration, so the names they emit are
   mapped onto design tokens here instead.

   Most of what they emit (.d-none, .form-control*, .form-select-sm, .btn-sm,
   .btn-outline-*, .list-group*, .searchable-select-*, .text-end, .text-center,
   .py-4, .is-hidden-initial) is already defined by the legacy-shim blocks
   further down, so only the names nothing else defines are added here.

   Nothing in a hand-written .html uses these names. Delete an entry once the
   script that was its last user is rewritten.
   ========================================================================== */

/* --- The "Composite" marker on an ingredient row -------------------------- */
.badge { /* = .chip */
  display: inline-flex;
  align-items: center;
  height: 1.375rem;
  padding-inline: var(--space-sm);
  border-radius: var(--radius-sm);
  border: var(--border-width) solid transparent;
  font-size: var(--font-size-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  white-space: nowrap;
}
.text-bg-secondary { /* = .chip--neutral */
  background: var(--color-surface-sunken);
  border-color: var(--color-border-strong);
  color: var(--color-slate-600);
}

/* --- The Excel-import banner ----------------------------------------------
   The scripts replace the element's className outright with `alert alert-success`
   / `alert alert-warning` and write newline-separated text into it, so the
   variant has to carry the block + pre-line treatment as well as the colour. */
.alert-success,
.alert-warning,
.alert-danger,
.alert-info {
  display: block;
  white-space: pre-line;
}
.alert-success { background: var(--color-primary-soft);   border-color: var(--color-primary-border);   color: var(--color-primary-text); }
.alert-warning { background: var(--color-secondary-soft);  border-color: var(--color-secondary-border); color: var(--color-secondary-text); }
.alert-danger  { background: var(--color-danger-soft);     border-color: var(--color-danger-border);    color: var(--color-danger-text); }
.alert-info    { background: var(--color-tertiary-soft);   border-color: var(--color-tertiary-border);  color: var(--color-tertiary-text); }

/* The trailing cost on a selected recipe row should not stay grey. */
.list-group-item.active .text-muted { color: inherit; }


/* ==========================================================================
   APPENDED — VENDORS / VENDOR-MAPPING / INDENTS / LOGIN MIGRATION
   --------------------------------------------------------------------------
   Two kinds of rule live here.

   (A) New design-system classes these four screens needed and the catalogue
       above did not have (sign-in screen, a width-limited search field, a
       scrollable checkbox panel, a wide table column, a script-toggleable
       hidden state).

   (B) A compatibility layer for markup that is still generated by JavaScript
       we were told not to touch — shared/searchable-select.js, and the row /
       list builders inside vendors.js, vendor-mapping.js and indents.js. Those
       scripts emit the old Bootstrap class names. Rather than rewrite working
       behaviour just to rename a class, the old names are mapped onto the
       design system's own tokens here, so JS-built rows look native. When
       those scripts are eventually rewritten to emit design-system classes,
       block (B) can be deleted wholesale.
   ========================================================================== */


/* ---- (A) New design-system classes --------------------------------------- */

/* The sign-in screen has grown its own section — see 20 at the foot of this
   file. Nothing is defined for it here any more. */

/* A .search-field that must not eat the whole row — e.g. sitting in a
   .card__header next to the card title. */
.search-field--compact {
  width: 20rem;
  max-width: 100%;
}

/* A bordered, scrolling panel of .form-check rows (the vendor-mapping
   ingredient picker). The DS has .scroll-area for the scrolling and
   .form-check for the rows, but nothing that frames the two together. */
.check-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  max-height: 30rem;
  padding: var(--space-sm);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
}

/* Give one .data-table column the lion's share of the width, for tables whose
   first column holds a picker and the rest hold short values. */
.data-table__col-wide { width: 40%; }

/* Hidden, but NOT with !important — deliberately different from .is-hidden.
   Some page scripts reveal an element by assigning element.style.display
   directly; an !important rule would beat that inline style and the element
   would never appear. This class only sets the initial state and steps aside
   the moment a script sets a display of its own. */
.is-hidden-initial { display: none; }


/* ---- (B) Legacy class compatibility -------------------------------------- */

/* Toggled by shared/searchable-select.js, login.js and indents.js. */
.d-none { display: none !important; }

/* Emitted by the list builder in indents.js. */
.d-flex                  { display: flex; }
.justify-content-between { justify-content: space-between; }
.align-items-center      { align-items: center; }

/* Cell / text helpers emitted by the row builders. */
.text-center  { text-align: center; }
.text-end     { text-align: right; }
.text-success { color: var(--color-primary-text); }
.small        { font-size: var(--font-size-body-sm); line-height: var(--line-height-body-sm); }
.py-3         { padding-block: var(--space-md); }
.py-4         { padding-block: var(--space-base); }

/* Legacy form controls — same look as .form-input / .form-select. */
.form-control {
  width: 100%;
  min-width: 0;
  height: var(--control-height);
  padding-inline: var(--space-md);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  box-shadow: var(--shadow-xs);
  transition: var(--transition-colors);
}
.form-control::placeholder { color: var(--color-text-subtle); }
.form-control:hover { border-color: var(--color-slate-400); }
.form-control:focus,
.form-control:focus-visible {
  outline: none;
  border-color: var(--color-focus);
  box-shadow: var(--shadow-focus);
  background-color: var(--color-focus-tint);
}
.form-control:disabled,
.form-control[readonly] {
  background: var(--color-surface-sunken);
  color: var(--color-text-subtle);
  cursor: not-allowed;
  box-shadow: none;
}
.form-control-sm,
.form-select-sm {
  height: var(--control-height-sm);
  font-size: var(--font-size-body-sm);
  padding-inline: var(--space-sm);
}
.form-select-sm { padding-right: 2rem; background-position: right var(--space-sm) center; }

/* Legacy checkbox / radio — same treatment as .form-check__input. */
.form-check-input {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-xs);
  background: var(--color-surface);
  cursor: pointer;
  display: inline-block;
  position: relative;
  vertical-align: middle;
  transition: var(--transition-colors);
}
.form-check-input[type="radio"] { border-radius: var(--radius-pill); }
.form-check-input:hover { border-color: var(--color-primary); }
.form-check-input:checked {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  background-size: 0.75rem 0.75rem;
  background-position: center;
  background-repeat: no-repeat;
}
.form-check-input[type="radio"]:checked {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='6' fill='%23ffffff'/%3E%3C/svg%3E");
  background-size: 0.625rem 0.625rem;
}
.form-check-input:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.form-check-label {
  font-size: var(--font-size-body-md);
  color: var(--color-text-secondary);
  cursor: pointer;
  min-width: 0;
}

/* Legacy buttons — mapped onto the .btn variants. These always appear
   alongside .btn, which supplies the shape; only the skin is set here. */
.btn-sm {
  height: var(--control-height-sm);
  padding-inline: var(--space-md);
  font-size: var(--font-size-body-sm);
  gap: var(--space-2xs);
}
.btn-outline-secondary {
  background: var(--color-surface);
  color: var(--color-text-secondary);
  border-color: var(--color-border);
  box-shadow: var(--shadow-xs);
}
.btn-outline-secondary:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
  border-color: var(--color-border-hover);
}
.btn-outline-danger {
  background: var(--color-danger-soft);
  color: var(--color-danger);
  border-color: var(--color-danger-border);
}
.btn-outline-danger:hover { background: var(--color-danger-soft-alt); color: var(--color-danger-hover); }
.btn-outline-success {
  background: var(--color-primary-soft);
  color: var(--color-primary-text);
  border-color: var(--color-primary-border);
}
.btn-outline-success:hover { background: var(--color-primary-tint); }

/* Legacy list group — the selectable vendor / indent lists are still built as
   <li class="list-group-item">, so the component is defined here. */
.list-group {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 0;
}
.list-group-flush { border: 0; }
.list-group-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-base);
  border-top: var(--border-width) solid var(--color-border-subtle);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  color: var(--color-text-secondary);
  transition: var(--transition-colors);
  min-width: 0;
}
.list-group-item:first-child { border-top: 0; }
.list-group-item:hover { background: var(--color-surface-hover); color: var(--color-text); }
.list-group-item.active {
  background: var(--color-primary-soft);
  color: var(--color-primary-text);
  font-weight: var(--font-weight-semibold);
  box-shadow: inset 3px 0 0 0 var(--color-primary);
}

/* Type-to-filter dropdown built by shared/searchable-select.js. The native
   <select> stays in the DOM (so required-field validation still works) but is
   visually hidden; a .form-select-looking text input stands in front of it. */
.searchable-select-wrapper { position: relative; min-width: 0; }
.searchable-select-native {
  position: absolute;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
  box-shadow: none;
  opacity: 0;
  pointer-events: none;
}
.searchable-select-input { cursor: text; }
.searchable-select-dropdown {
  position: fixed;
  z-index: var(--z-dropdown);
  max-height: 16rem;
  padding-block: var(--space-xs);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
}
.searchable-select-option {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  color: var(--color-text-secondary);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: var(--transition-colors);
}
.searchable-select-option:hover,
.searchable-select-option.highlighted {
  background: var(--color-primary-soft);
  color: var(--color-primary-text);
}
.searchable-select-option.disabled { color: var(--color-text-subtle); cursor: not-allowed; }
.searchable-select-option.disabled:hover { background: transparent; color: var(--color-text-subtle); }
.searchable-select-group-label {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-subtle);
}
.searchable-select-empty {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-size-body-md);
  color: var(--color-text-muted);
}


/* ==========================================================================
   APPENDED — COMBOS / MENU / ADD ONS MIGRATION
   --------------------------------------------------------------------------
   combos.js, menu.js and addons.js build their editor groups, table rows and
   add-on picker rows in JavaScript. Those files carry page behaviour and were
   deliberately left untouched by this visual migration, so the class names in
   their template literals are still the Bootstrap ones.

   The two legacy-shim blocks appended above already cover most of what those
   scripts emit (.d-none, .form-control*, .form-check-*, .btn-sm,
   .btn-outline-*, .list-group*, .alert-*, .searchable-select-*, .d-flex,
   .text-end/.text-center, .small, .is-hidden-initial). Only the names those
   blocks do NOT define are added here, so there is one definition of each.

   Nothing in the hand-written .html uses these names — new markup uses the
   catalogue at the top of this file. Delete an entry once the script that was
   its last user is rewritten.
   ========================================================================== */

/* --- Revealing a button by inline style ----------------------------------
   combos.js shows #deleteComboBtn with style.display = 'inline-block'. That
   inline value replaces .btn's own inline-flex and drops the label out of the
   control's vertical centre, and it cannot be overridden without !important —
   which would then also defeat style.display = 'none'. Matching on the inline
   value itself restores the flex box only while the button is shown. */
.btn[style*="inline-block"] { display: inline-flex !important; }

/* --- The primary action colour the old stylesheet called "brand" ---------- */
.btn-brand { /* = .btn--primary */
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-color: var(--color-primary);
  box-shadow: var(--shadow-card);
}
.btn-brand:hover  { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
.btn-brand:active { background: var(--color-primary-active); border-color: var(--color-primary-active); }

/* --- The per-group cards on the combos editor ----------------------------
   Built as <div class="card mb-2"><div class="card-body p-2">, i.e. the
   design system's own .card wrapping a legacy body. */
.card-body      { padding: var(--space-base); min-width: 0; }
.card-body.p-2  { padding: var(--space-sm); }
.p-2            { padding: var(--space-sm); }

/* --- The item table inside each combo group ------------------------------- */
.table > thead > tr > th {
  padding: var(--space-sm) var(--space-md);
  text-align: left;
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-surface-sunken);
  border-bottom: var(--border-width) solid var(--color-border);
}
.table > tbody > tr { border-top: var(--border-width) solid var(--color-border-subtle); }
.table > tbody > tr:first-child { border-top: 0; }
.table > tbody > tr > td {
  padding: var(--space-sm) var(--space-md);
  font-size: var(--font-size-body-md);
  line-height: var(--line-height-body-md);
  color: var(--color-text-secondary);
  vertical-align: middle;
}
.table-sm > tbody > tr > td,
.table-sm > thead > tr > th { padding: var(--space-xs) var(--space-sm); }
.align-middle,
.align-middle > tbody > tr > td,
.align-middle > thead > tr > th { vertical-align: middle; }

/* --- Spacing utilities the script-built rows rely on ---------------------- */
.gap-1 { gap: var(--space-xs); }
.gap-2 { gap: var(--space-sm); }
.gap-3 { gap: var(--space-md); }
.mb-0  { margin-bottom: 0; }
.mb-1  { margin-bottom: var(--space-xs); }
.mb-2  { margin-bottom: var(--space-sm); }
.mb-3  { margin-bottom: var(--space-md); }
.mt-1  { margin-top: var(--space-xs); }
.mt-2  { margin-top: var(--space-sm); }
.ms-1  { margin-left: var(--space-xs); }
.ms-auto { margin-left: auto; }
.pt-2  { padding-top: var(--space-sm); }
.pt-3  { padding-top: var(--space-md); }
.border-top  { border-top: var(--border-width) solid var(--color-border-subtle); }
.flex-grow-1 { flex: 1 1 auto; min-width: 0; }

/* --- Text utilities the script-built rows rely on ------------------------- */
.fw-semibold     { font-weight: var(--font-weight-semibold); }
.text-nowrap     { white-space: nowrap; }
.text-capitalize { text-transform: capitalize; }
.text-truncate   { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- The combo picker list ------------------------------------------------
   combos.js builds each <li> as two rows — a name + cost line and the truncated
   write-up — but tags the <li> itself .d-flex, which lays those two rows out
   side by side and squeezes both in this narrow column. Stack them again,
   scoped to that one list. */
#comboList .list-group-item {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2xs);
}

/* --- Numeric columns in script-built rows ---------------------------------
   Several page scripts (menu.js, ingredients.js, recipes.js, vendors.js) build
   their <tr>s positionally and emit bare <td>s, so they cannot tag a money or
   quantity cell with .data-table__num the way hand-written markup does — which
   left right-aligned headers sitting over left-aligned numbers.

   Marking the <th> is therefore enough on its own: each cell inherits the
   alignment of the header it sits under — .data-table__num for a money or
   quantity column, .data-table__actions for a trailing column of row buttons. Covers the first 16 columns, the
   widest table in the app (ingredients). This replaces an earlier rule keyed on
   #tableBody, which silently reached three different screens that happen to
   share that id. */
.data-table:has(thead th:nth-child(1).data-table__num) tbody td:nth-child(1),
.data-table:has(thead th:nth-child(2).data-table__num) tbody td:nth-child(2),
.data-table:has(thead th:nth-child(3).data-table__num) tbody td:nth-child(3),
.data-table:has(thead th:nth-child(4).data-table__num) tbody td:nth-child(4),
.data-table:has(thead th:nth-child(5).data-table__num) tbody td:nth-child(5),
.data-table:has(thead th:nth-child(6).data-table__num) tbody td:nth-child(6),
.data-table:has(thead th:nth-child(7).data-table__num) tbody td:nth-child(7),
.data-table:has(thead th:nth-child(8).data-table__num) tbody td:nth-child(8),
.data-table:has(thead th:nth-child(9).data-table__num) tbody td:nth-child(9),
.data-table:has(thead th:nth-child(10).data-table__num) tbody td:nth-child(10),
.data-table:has(thead th:nth-child(11).data-table__num) tbody td:nth-child(11),
.data-table:has(thead th:nth-child(12).data-table__num) tbody td:nth-child(12),
.data-table:has(thead th:nth-child(13).data-table__num) tbody td:nth-child(13),
.data-table:has(thead th:nth-child(14).data-table__num) tbody td:nth-child(14),
.data-table:has(thead th:nth-child(15).data-table__num) tbody td:nth-child(15),
.data-table:has(thead th:nth-child(16).data-table__num) tbody td:nth-child(16) {
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-md);
  font-weight: var(--font-weight-numeric-md);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--font-feature-tnum);
  white-space: nowrap;
}

.data-table:has(thead th:nth-child(1).data-table__actions) tbody td:nth-child(1),
.data-table:has(thead th:nth-child(2).data-table__actions) tbody td:nth-child(2),
.data-table:has(thead th:nth-child(3).data-table__actions) tbody td:nth-child(3),
.data-table:has(thead th:nth-child(4).data-table__actions) tbody td:nth-child(4),
.data-table:has(thead th:nth-child(5).data-table__actions) tbody td:nth-child(5),
.data-table:has(thead th:nth-child(6).data-table__actions) tbody td:nth-child(6),
.data-table:has(thead th:nth-child(7).data-table__actions) tbody td:nth-child(7),
.data-table:has(thead th:nth-child(8).data-table__actions) tbody td:nth-child(8),
.data-table:has(thead th:nth-child(9).data-table__actions) tbody td:nth-child(9),
.data-table:has(thead th:nth-child(10).data-table__actions) tbody td:nth-child(10),
.data-table:has(thead th:nth-child(11).data-table__actions) tbody td:nth-child(11),
.data-table:has(thead th:nth-child(12).data-table__actions) tbody td:nth-child(12),
.data-table:has(thead th:nth-child(13).data-table__actions) tbody td:nth-child(13),
.data-table:has(thead th:nth-child(14).data-table__actions) tbody td:nth-child(14),
.data-table:has(thead th:nth-child(15).data-table__actions) tbody td:nth-child(15),
.data-table:has(thead th:nth-child(16).data-table__actions) tbody td:nth-child(16) {
  text-align: right;
  white-space: nowrap;
}

/* ---- Appended for menu.html (combos / menu / add ons migration) ----------
   .menu-table — the Menu grid, whose three price columns each hold a number
   <input> built by menu.js. A text input's default intrinsic width (about 20
   characters) would otherwise claim most of the table and squeeze the item
   name down to two words per line. Scoped to this one table by class rather
   than by the #tableBody id, which several screens share. */
.menu-table tbody td:nth-child(n+5):nth-child(-n+7) { width: 7.5rem; }
.menu-table tbody td input.form-control { width: 100%; min-width: 5.5rem; }

/* =============================================================================
   ACCESS CONTROL
   -----------------------------------------------------------------------------
   Classes the shell uses to reflect what the signed-in user may do.
   ========================================================================== */

/* Holds the permission-filtered nav sections. Built after the user's access loads, so it
   inherits the spacing .app-sidebar__top gives its direct children. */
.app-sidebar__navhost {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  min-height: 0;
  overflow-y: auto;
}

/* Hiding actions the user isn't allowed to take.
   The body carries perm-no-create / perm-no-edit / perm-no-delete for the current screen,
   and perm-read-only when they can neither create nor edit (so there is nothing to save).

   New screens mark buttons with data-requires="create|edit|delete|write".
   The older screens predate that, so their buttons are listed by id/class instead.

   !important is deliberate: several page scripts reveal buttons by setting
   element.style.display, and an inline style would otherwise put a forbidden button back.
   This is convenience only — main.js refuses the underlying call either way. */
body.perm-no-create [data-requires~="create"],
body.perm-no-edit [data-requires~="edit"],
body.perm-no-delete [data-requires~="delete"],
/* A rank rather than a permission — see db/roles.js. `ops` needs an operations manager or
   the owner (L2); `manager` needs any of the three manager roles (L3). Named after the rank
   rather than the feature, so the next thing that needs one reuses a token instead of
   inventing another. */
body.perm-no-ops [data-requires~="ops"],
body.perm-no-manager [data-requires~="manager"],
body.perm-read-only [data-requires~="write"],

body.perm-no-create :is(#addBtn, #newRecipeBtn, #newComboBtn, #newIndentBtn, #addToMenuBtn),
/* Excel import both creates and updates, so it needs both permissions. */
body.perm-no-create #importBtn,
body.perm-no-edit #importBtn,

body.perm-no-delete :is(.delete-btn, #deleteComboBtn, #deleteIndentBtn, #deleteRecipeBtn),

body.perm-read-only :is(
  #saveBtn, #saveRecipeBtn, #saveComboBtn, #saveIndentBtn, #saveMappingBtn, #saveAddonsBtn,
  .save-row-btn,
  #addRowBtn, #addItemBtn, #addContactBtn, #addComponentBtn, #addGroupBtn,
  .remove-row-btn, .remove-item-btn, .remove-contact-btn, .remove-component-btn, .remove-group-btn
) {
  display: none !important;
}

/* The permission grid on the Permissions screen: page names down the side, the four
   actions across, one checkbox per cell. */
.perm-grid th:not(:first-child),
.perm-grid td:not(:first-child) {
  text-align: center;
  width: 7rem;
}
.perm-grid td:not(:first-child) .form-check {
  justify-content: center;
}
.perm-grid__section td {
  background: var(--color-surface-sunken);
  font-size: var(--font-size-label-caps);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--letter-spacing-label-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* A chip that opens something when clicked — e.g. an add-on group on the Menu, which shows
   that group's add-ons. Looks like a chip, behaves like a button. */
/* Row-by-row problems found in an uploaded sheet, shown in a modal. */
.problem-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  max-height: 22rem;
  overflow-y: auto;
  margin: var(--space-sm) 0 0;
  padding-left: var(--space-lg);
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text);
}

/* A number typed straight into a table cell (prices, portions) and saved on Enter/leave. */
.form-input--cell {
  width: 6.5rem;
  min-width: 5rem;
  text-align: right;
}

.chip--action {
  cursor: pointer;
  font: inherit;
  font-size: var(--font-size-label-caps);
  border-style: solid;
  transition: var(--transition-colors);
}
.chip--action:hover {
  background: var(--color-primary-soft);
  border-color: var(--color-primary-border);
  color: var(--color-primary-text);
}
.chip--action:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: 1px;
}

/* ==========================================================================
   COLLAPSIBLE SIDEBAR
   .app-shell.is-sidebar-collapsed shrinks the sidebar to an icon rail so pages get the
   full width. Toggled by .sidebar-toggle in the topbar (or Ctrl+B) and remembered per
   computer. Each nav link carries a title, so its name shows on hover while collapsed.
   ========================================================================== */
.app-shell {
  transition: grid-template-columns var(--duration-base) ease;
}
.app-shell.is-sidebar-collapsed { grid-template-columns: var(--sidebar-rail-width) minmax(0, 1fr); }
.app-shell.is-sidebar-collapsed .app-sidebar { align-items: center; }
.app-shell.is-sidebar-collapsed .app-sidebar__top { align-items: center; width: 100%; }
.app-shell.is-sidebar-collapsed .app-sidebar__navhost { width: 100%; padding-inline: var(--space-sm); gap: var(--space-sm); }
.app-shell.is-sidebar-collapsed .app-sidebar__brand { justify-content: center; padding-inline: var(--space-sm); }
.app-shell.is-sidebar-collapsed .app-sidebar__brand-text,
.app-shell.is-sidebar-collapsed .app-sidebar__section-label,
.app-shell.is-sidebar-collapsed .app-sidebar__meta,
.app-shell.is-sidebar-collapsed .app-sidebar .nav-item__label,
.app-shell.is-sidebar-collapsed .app-sidebar .nav-item__badge { display: none; }
.app-shell.is-sidebar-collapsed .app-sidebar .nav-item { justify-content: center; padding-inline: var(--space-sm); border-right-width: 0; }
/* With the section names hidden, a thin rule keeps the groups apart. */
.app-shell.is-sidebar-collapsed .app-sidebar__group + .app-sidebar__group {
  padding-top: var(--space-sm);
  border-top: var(--border-width) solid var(--color-border-subtle);
}

/* Hovering the collapsed rail (or tabbing into it) opens it to full width OVER the page —
   the content doesn't move — and it folds back when the pointer leaves. The short delay
   before opening stops it flicking open when the pointer only passes across it. */
.app-shell.is-sidebar-collapsed .app-sidebar {
  width: var(--sidebar-rail-width);
  transition: width var(--duration-base) ease, box-shadow var(--duration-base) ease;
}
.app-shell.is-sidebar-collapsed .app-sidebar:hover,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within {
  width: var(--sidebar-width);
  align-items: stretch;
  box-shadow: var(--shadow-popover);
  transition-delay: 150ms;
}
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__top,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__top { align-items: stretch; }
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__navhost,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__navhost { padding-inline: 0; gap: var(--space-sm); }
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__brand,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__brand { justify-content: flex-start; padding-inline: var(--space-md); }
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__brand-text,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__brand-text,
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__meta,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__meta { display: flex; }
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__section-label,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__section-label,
.app-shell.is-sidebar-collapsed .app-sidebar:hover .nav-item__label,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .nav-item__label,
.app-shell.is-sidebar-collapsed .app-sidebar:hover .nav-item__badge,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .nav-item__badge { display: block; white-space: nowrap; }
.app-shell.is-sidebar-collapsed .app-sidebar:hover .nav-item,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .nav-item {
  justify-content: flex-start;
  padding-inline: var(--space-md);
  border-right-width: var(--border-width-thick);
}
.app-shell.is-sidebar-collapsed .app-sidebar:hover .app-sidebar__group + .app-sidebar__group,
.app-shell.is-sidebar-collapsed .app-sidebar:focus-within .app-sidebar__group + .app-sidebar__group { padding-top: 0; border-top: 0; }
/* The folded-away rail shows every page's icon, whatever categories are closed. */
.app-shell.is-sidebar-collapsed .app-sidebar:not(:hover):not(:focus-within) .app-sidebar__group.is-closed .app-sidebar__nav { display: flex; }

.sidebar-toggle {
  flex: 0 0 auto;
  color: var(--color-text-muted);
}
.sidebar-toggle:hover { color: var(--color-text); }

/* ==========================================================================
   COLLAPSIBLE NAV CATEGORIES
   Each category is an .app-sidebar__group: a heading button (.app-sidebar__section-toggle,
   styled like .app-sidebar__section-label) and its .app-sidebar__nav. .is-closed folds the
   links away. Remembered per computer; the category of the current page is always open.
   ========================================================================== */
.app-sidebar__group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
/* Compact spacing so the whole menu fits without scrolling: 8px between categories,
   2px between links, and shorter links. */
.app-sidebar__navhost { gap: var(--space-sm); }
.app-sidebar__nav { gap: var(--space-2xs); }
.app-sidebar .nav-item { padding-block: var(--space-xs); }
.app-sidebar__section-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  width: 100%;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: var(--transition-colors);
}
.app-sidebar__section-toggle:hover { color: var(--color-primary); background: var(--color-primary-soft); }
.app-sidebar__section-toggle:focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: -2px;
}
.app-sidebar__section-chevron {
  flex: 0 0 auto;
  font-size: 18px;
  transition: transform var(--duration-fast) ease;
}
.app-sidebar__group.is-closed .app-sidebar__section-chevron { transform: rotate(-90deg); }
.app-sidebar__group.is-closed .app-sidebar__nav { display: none; }
/* Below 1024px the sidebar is always the icon rail: no headings, and every page's icon
   shows even in a category that was folded away on a wider window. */
@media (max-width: 1023.98px) {
  .app-sidebar__section-toggle { display: none; }
  .app-sidebar__group.is-closed .app-sidebar__nav { display: flex; }
  .app-sidebar__group + .app-sidebar__group {
    padding-top: var(--space-sm);
    border-top: var(--border-width) solid var(--color-border-subtle);
  }
}

/* ==========================================================================
   LINE GRID — item lines in a dialog (goods receipt, add-on portions, stock corrections)
   One heading row (.line-grid__head) and one .line-grid__row per line, all sharing the same
   columns so the boxes line up down the page. The modifier sets the columns:
     --receipt  Ingredient | Qty | UOM | Unit cost | remove
     --portion  Ingredient | Portion | UOM | remove
     --qty      Ingredient | Qty | remove
   Below 768px each line stacks again.
   ========================================================================== */
.line-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.line-grid__head,
.line-grid__row {
  display: grid;
  gap: var(--space-sm);
  align-items: center;
}
/* A goods receipt line now carries what the bill said, what we paid and what the setup
   expected, side by side — the point being that a price which has moved is obvious while
   the delivery is still a draft, rather than a month later in the food cost. */
.line-grid--receipt .line-grid__head,
.line-grid--receipt .line-grid__row {
  grid-template-columns:
    minmax(11rem, 2.6fr)    /* their product     */
    minmax(9rem, 2fr)       /* our ingredient    */
    minmax(4.5rem, 0.7fr)   /* their unit        */
    minmax(5rem, 0.8fr)     /* qty               */
    minmax(6rem, 0.9fr)     /* our unit          */
    minmax(6.5rem, 1fr)     /* unit cost + GST   */
    minmax(5rem, 0.8fr)     /* delivery share    */
    minmax(6rem, 0.9fr)     /* setup says        */
    minmax(6rem, 0.9fr)     /* difference        */
    minmax(6.5rem, 1fr)     /* suggested cost    */
    var(--control-height-sm);
  min-width: 77rem;
}
/* Ten columns do not fit every window. The block scrolls sideways as one, so the headings
   stay over their own figures instead of drifting off them. */
.line-grid--receipt { overflow-x: auto; }

/* Their product name: a record of what arrived, shown rather than offered for editing. It
   wraps, because "Coca Cola - Diet Coke, 330 ml Can (Pack of 24) MRP - 50/pc" cut off at
   the width of a column is the half that matters missing again. */
.line-grid__name {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text);
  overflow-wrap: anywhere;
  white-space: normal;
}
.line-grid__name--quiet { color: var(--color-text-subtle); }
/* The file the line was read out of, under the name it was read as. Small and quiet: it is
   provenance, not information you read every time — but a month from now it is the only
   thing that answers "where did this figure come from". */
.line-grid__source {
  display: block;
  /* A step below the name it sits under — label-caps is the smallest size the type ladder
     has, and this is the quietest thing on the row. */
  font-size: var(--font-size-label-caps);
  line-height: 1.15;
  color: var(--color-text-subtle);
  overflow-wrap: anywhere;
}
.form-input--readonly {
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  cursor: default;
}
/* The figures the app worked out rather than the ones anybody typed. Readable, plainly not
   a box to fill in. */
.line-grid__derived {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--font-size-numeric-sm);
  text-align: right;
  color: var(--color-text-secondary);
  padding-right: var(--space-2xs);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.line-grid__derived--up   { color: var(--color-danger-text); font-weight: var(--font-weight-semibold); }
.line-grid__derived--down { color: var(--color-success-text); font-weight: var(--font-weight-semibold); }
.line-grid__derived--quiet { color: var(--color-text-subtle); }
/* A row a filter has set aside. Hidden rather than removed, so the figures it holds are
   still there when the filter is switched off. */
.line-grid__row--hidden { display: none; }
/* A whole row read off a bill. Nothing in it is typed, so nothing in it invites typing. */
.line-grid__row--locked .form-input,
.line-grid__row--locked .form-select {
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  border-color: var(--color-border);
}
.line-grid--portion .line-grid__head,
.line-grid--portion .line-grid__row { grid-template-columns: minmax(0, 3fr) minmax(6rem, 1fr) minmax(5rem, 1fr) var(--control-height-sm); }
.line-grid--qty .line-grid__head,
.line-grid--qty .line-grid__row { grid-template-columns: minmax(0, 3fr) minmax(6rem, 1fr) var(--control-height-sm); }
.line-grid__head {
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.line-grid__num { text-align: right; }
.line-grid__row > * { min-width: 0; width: 100%; }
.line-grid__row > .btn--icon { width: var(--control-height-sm); }
.line-grid__empty { color: var(--color-text-muted); font-size: var(--font-size-body-sm); }

/* ==========================================================================
   PHONES — the sidebar becomes a drawer
   256px of navigation out of 375px of screen leaves nothing for the screen, so below
   768px the shell stops being two columns: the sidebar lifts out of the grid and slides
   over the page when the topbar's toggle is pressed. The SAME .is-sidebar-collapsed class
   drives it, meaning "rail" on a desktop and "shut" here — one piece of state, so the two
   can never disagree. shell.js starts it shut on every phone load and never writes that
   state down, so the desktop's own preference survives.
   ========================================================================== */
@media (max-width: 767.98px) {
  /* Both states, because .app-shell.is-sidebar-collapsed sets the rail track at a higher
     specificity further up and would otherwise keep a two-column grid here — with the
     sidebar lifted out of flow, .app-main would drop into the rail track and be 56px wide. */
  .app-shell,
  .app-shell.is-sidebar-collapsed { grid-template-columns: minmax(0, 1fr); transition: none; }

  .app-sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: min(17rem, 82vw);
    z-index: var(--z-modal);
    box-shadow: var(--shadow-popover);
    transition: transform var(--duration-base) ease;
  }
  /* Shut by default; open only while the drawer is out. */
  .app-shell.is-sidebar-collapsed .app-sidebar { transform: translateX(-100%); }
  .app-shell .app-sidebar { transform: translateX(0); }
  .app-shell.is-sidebar-collapsed .app-sidebar { width: min(17rem, 82vw); align-items: stretch; }

  /* The desktop rail's hover-to-peek would fight a drawer that is meant to stay shut until
     it is asked for, and a finger cannot hover anyway. */
  .app-shell.is-sidebar-collapsed .app-sidebar:hover,
  .app-shell.is-sidebar-collapsed .app-sidebar:focus-within { width: min(17rem, 82vw); transition-delay: 0s; }
  /* While shut the drawer is off-screen, so the desktop rail's icon-only styling can stay
     on it — but it must keep its full width, or it would slide back in as a 56px strip. */
  .app-shell.is-sidebar-collapsed .app-sidebar { align-items: stretch; }
  .app-shell.is-sidebar-collapsed .app-sidebar__navhost { padding-inline: 0; }

  .app-sidebar__backdrop {
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-modal) - 1);
    background: rgb(0 0 0 / 45%);
    opacity: 1;
    transition: opacity var(--duration-base) ease;
  }
  .app-shell.is-sidebar-collapsed .app-sidebar__backdrop { opacity: 0; pointer-events: none; }

  /* Room to read. The desktop gutter is generous because it can afford to be. */
  .app-content { padding: var(--space-base) var(--space-base) var(--space-xl); gap: var(--space-base); }
  .app-topbar { padding-inline: var(--space-base); gap: var(--space-sm); }

  /* A segmented control is as wide as its options, which on a phone is wider than the
     screen. It scrolls sideways rather than having its last option cut off the edge where
     nothing can reach it. (.tabs already does this.) */
  .segmented {
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .segmented::-webkit-scrollbar { display: none; }
  .segmented__option { flex: 0 0 auto; }

  /* A card's buttons sit beside its title on a desktop. Here they drop under it and wrap
     among themselves, because "flex: 0 0 auto" in a row narrower than they are pushes the
     last one off the side. */
  .card__header { flex-wrap: wrap; }
  .card__actions { flex-wrap: wrap; flex: 1 1 auto; }

  /* A flex item will not shrink below its content unless it is told it may. Several
     headers wrap their controls in a plain <div>, which then holds the whole row at its
     natural width and carries the last control off the screen — even though what is
     inside it was made scrollable. */
  .page-header__actions > *,
  .card__actions > * { min-width: 0; max-width: 100%; }

  /* The topbar carries the menu toggle, the outlet, who is signed in, Password and Logout.
     That is more than 375px holds, and squeezing them turns "The Bunhouse Cafe" into "Th".
     The outlet goes: there is one of it, and the page title is directly underneath. (The
     name beside the avatar is already hidden at this width, further up.) */
  .app-topbar .context-switch { display: none; }
  .app-topbar__start,
  .app-topbar__end { min-width: 0; }
}

/* The backdrop only exists for the drawer, so it is out of the way everywhere else. */
.app-sidebar__backdrop { display: none; }
@media (max-width: 767.98px) { .app-sidebar__backdrop { display: block; } }

@media (max-width: 767.98px) {
  .line-grid__head { display: none; }
  .line-grid--receipt .line-grid__row,
  .line-grid--portion .line-grid__row,
  .line-grid--qty .line-grid__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .line-grid__row > :first-child { grid-column: 1 / -1; }
}

/* ==========================================================================
   DOWNLOAD GUIDE — "how to get this report out of Petpooja"
   .guide holds a title, the menu path (.guide__path, one .guide__crumb per menu with an
   arrow between), and the date range to enter (.guide__range with .guide__date values).
   ========================================================================== */
.guide {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-base);
  border: var(--border-width) solid var(--color-info-border);
  border-radius: var(--radius-lg);
  background: var(--color-info-soft);
  color: var(--color-text);
}
.guide__title {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--font-size-title-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-info-text);
}
.guide__step {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.guide__label {
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.guide__path {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}
.guide__crumb {
  padding: var(--space-2xs) var(--space-sm);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  font-size: var(--font-size-body-sm);
  font-weight: var(--font-weight-semibold);
}
.guide__arrow { color: var(--color-text-subtle); font-size: 18px; }
.guide__range {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--font-size-body-sm);
}
.guide__date {
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-primary-border);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  font-weight: var(--font-weight-semibold);
  color: var(--color-primary-text);
}
.guide__note { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }

/* ==========================================================================
   MATRIX TABLE — a .data-table with totals (Sales Dashboard: channel × payment type)
   .data-table tr.is-total      the totals row: bold, ruled off above
   .data-table td.is-row-total  the right-hand totals column
   .matrix-cell                 an amount with its order count underneath
   .matrix-cell.is-empty        a zero cell, dimmed so the real figures stand out
   ========================================================================== */
.data-table tbody tr.is-total td {
  font-weight: var(--font-weight-bold);
  border-top: var(--border-width-thick) solid var(--color-border-strong);
  background: var(--color-surface-sunken);
}
.data-table td.is-row-total { font-weight: var(--font-weight-bold); background: var(--color-surface-sunken); }
.matrix-cell {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2xs);
}
.matrix-cell__amount { font-variant-numeric: tabular-nums; font-feature-settings: var(--font-feature-tnum); }
/* Amount and its qualifier on one line, for a long table that has to fit the screen. The
   qualifier ("66 ord", the share) sits on the left and the amount stays hard against the
   right edge, so every figure in a column lines up under its heading. */
.matrix-cell--inline { flex-direction: row; align-items: baseline; justify-content: space-between; gap: var(--space-sm); }
.matrix-cell__note { display: inline-flex; align-items: baseline; gap: var(--space-xs); min-width: 0; }
.matrix-cell__count { font-size: var(--font-size-body-sm); color: var(--color-text-subtle); font-weight: var(--font-weight-regular); }
/* Share of total sales, on the totals — primary colour so it reads as the headline. */
.matrix-cell__share {
  font-size: var(--font-size-body-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-primary-text);
}
.matrix-cell.is-empty .matrix-cell__share { color: var(--color-text-subtle); font-weight: var(--font-weight-regular); }
.matrix-cell.is-empty .matrix-cell__amount { color: var(--color-text-subtle); font-weight: var(--font-weight-regular); }

/* Date filter bar: two date boxes and quick-pick chips. */
.date-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-sm);
}
.date-filter .form-field { min-width: 10rem; }
.date-filter__presets { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.date-filter__presets .chip.is-active { background: var(--color-primary-soft); border-color: var(--color-primary-border); color: var(--color-primary-text); }

/* A lettered heading line in a breakdown table (Zomato payout breakup: A, B, C…). */
.data-table tbody tr.is-section td { font-weight: var(--font-weight-semibold); background: var(--color-surface-sunken); }
/* A clickable table row that opens more detail. */
.data-table tbody tr.is-clickable { cursor: pointer; }
.data-table tbody tr.is-clickable:hover td { background: var(--color-primary-soft); }
/* Label / value pairs in a detail panel. */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--space-sm) var(--space-lg);
}
.detail-grid__item { display: flex; flex-direction: column; gap: var(--space-2xs); min-width: 0; }
.detail-grid__label {
  font-size: var(--font-size-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.detail-grid__value { font-size: var(--font-size-body-md); color: var(--color-text); overflow-wrap: anywhere; }

/* ==========================================================================
   TREND CHART — a small interactive line chart drawn as SVG (ui.lineChart)
   Hovering shows every series' value for that point in .chart__tooltip; the legend
   buttons (.chart__legend-item) switch a series off and on. Series colours are
   .chart__series--1 … --4.
   ========================================================================== */
.chart {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.chart__title {
  font-size: var(--font-size-label-caps);
  letter-spacing: var(--letter-spacing-label-caps);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.chart__svg { width: 100%; height: auto; display: block; overflow: visible; }
.chart__grid { stroke: var(--color-border-subtle); stroke-width: 1; }
.chart__axis-label { fill: var(--color-text-subtle); font-family: var(--font-mono); font-size: 10px; }
.chart__line { fill: none; stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.chart__dot { stroke: var(--color-surface); stroke-width: 1.5; }
.chart__band { fill: transparent; cursor: crosshair; }
.chart__band:hover, .chart__band.is-active { fill: var(--color-primary-soft); fill-opacity: 0.5; }
.chart__series--1 { stroke: var(--color-primary); fill: var(--color-primary); color: var(--color-primary); }
.chart__series--2 { stroke: var(--color-warning); fill: var(--color-warning); color: var(--color-warning); }
.chart__series--3 { stroke: var(--color-danger); fill: var(--color-danger); color: var(--color-danger); }
.chart__series--4 { stroke: var(--color-text-muted); fill: var(--color-text-muted); color: var(--color-text-muted); }
.chart__series--5 { stroke: var(--color-tertiary);  fill: var(--color-tertiary);  color: var(--color-tertiary); }
.chart__series--6 { stroke: var(--color-secondary); fill: var(--color-secondary); color: var(--color-secondary); }
/* A bar takes the series colour as its fill and draws no outline. */
.chart__bar { stroke: none; rx: 2; }
.chart__line.chart__series--1, .chart__line.chart__series--2, .chart__line.chart__series--3,
.chart__line.chart__series--4, .chart__line.chart__series--5, .chart__line.chart__series--6 { fill: none; }
.chart__legend { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.chart__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-2xs) var(--space-sm);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  font: inherit;
  font-size: var(--font-size-body-sm);
  color: var(--color-text);
  cursor: pointer;
}
.chart__legend-item.is-off { opacity: 0.45; text-decoration: line-through; }
.chart__swatch { width: 0.75rem; height: 0.25rem; border-radius: var(--radius-pill); background: currentColor; }
/* The swatch keeps its series colour inside a legend button that sets its own text colour.
   Without these the legend paints every swatch in the text colour and stops matching the
   lines it is naming. */
.chart__series--1 .chart__swatch { background: var(--color-primary); }
.chart__series--2 .chart__swatch { background: var(--color-warning); }
.chart__series--3 .chart__swatch { background: var(--color-danger); }
.chart__series--4 .chart__swatch { background: var(--color-text-muted); }
.chart__series--5 .chart__swatch { background: var(--color-tertiary); }
.chart__series--6 .chart__swatch { background: var(--color-secondary); }
.chart__tooltip {
  position: absolute;
  z-index: 2;
  min-width: 10rem;
  padding: var(--space-xs) var(--space-sm);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-popover);
  font-size: var(--font-size-body-sm);
  pointer-events: none;
}
.chart__tooltip-title { font-weight: var(--font-weight-semibold); margin-bottom: var(--space-2xs); }
.chart__tooltip-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm); }
.chart__tooltip-row span:last-child { font-family: var(--font-mono); }
.chart__empty { font-size: var(--font-size-body-sm); color: var(--color-text-muted); }

/* ==========================================================================
   18  DENSE PAGE — .page--dense on .app-content
   A reporting screen (Sales Dashboard) that has to be taken in at a glance. Rather
   than restyling each component, the page re-declares the type ladder and the spacing
   scale a step smaller: every card, table, label and gap inside it follows, because
   they are all built from these tokens. Figures stay legible — this is 13px body text
   and 12px table rows, not a screenshot shrunk.
   ========================================================================== */
.page--dense {
  /* Type — one step down the ladder, with matching line heights. */
  --font-size-headline-xl: 1.5rem;    --line-height-headline-xl: 1.875rem;
  --font-size-headline-lg: 1.125rem;  --line-height-headline-lg: 1.5rem;
  --font-size-headline-md: 1rem;      --line-height-headline-md: 1.375rem;
  --font-size-headline-sm: 0.875rem;  --line-height-headline-sm: 1.25rem;
  --font-size-title-sm:    0.75rem;   --line-height-title-sm:    1rem;
  --font-size-body-lg:     0.875rem;  --line-height-body-lg:     1.25rem;
  --font-size-body-md:     0.8125rem; --line-height-body-md:     1.125rem;
  --font-size-body-sm:     0.6875rem; --line-height-body-sm:     0.9375rem;
  --font-size-label-caps:  0.625rem;  --line-height-label-caps:  0.8125rem;
  --font-size-label-md:    0.75rem;   --line-height-label-md:    0.9375rem;
  --font-size-metric-val:  1.25rem;   --line-height-metric-val:  1.5rem;
  --font-size-numeric-lg:  1rem;      --line-height-numeric-lg:  1.375rem;
  --font-size-numeric-md:  0.8125rem; --line-height-numeric-md:  1.125rem;
  --font-size-numeric-sm:  0.6875rem; --line-height-numeric-sm:  0.875rem;

  /* Containers — the same rhythm at about two thirds the size. */
  --space-xs:   0.1875rem;  /*  3px */
  --space-sm:   0.3125rem;  /*  5px */
  --space-md:   0.5rem;     /*  8px */
  --space-base: 0.625rem;   /* 10px */
  --space-lg:   0.75rem;    /* 12px */
  --space-xl:   1rem;       /* 16px */
  --control-height-sm: 1.75rem;
  --control-height:    2rem;
  --table-row-height:  1.75rem;
  --chip-height:       1.25rem;
}

/* The few places that carry a hard-coded size instead of a token. */
.page--dense .data-table tbody td,
.page--dense .data-table--compact tbody td { height: auto; padding-block: 0.125rem; }
.page--dense .data-table thead th { padding-block: 0.1875rem; }
.page--dense .metric-card__icon { width: 1.75rem; height: 1.75rem; }
.page--dense .metric-card__icon .material-symbols-outlined { font-size: 16px; }
.page--dense .matrix-cell { gap: 0; }
.page--dense .card__body { gap: var(--space-sm); }
.page--dense .chart { gap: var(--space-2xs); }
.page--dense .metric-card--slim { padding: var(--space-xs) var(--space-md); }
.page--dense .metric-card--slim .metric-card__value {
  font-size: var(--font-size-metric-val);
  line-height: var(--line-height-metric-val);
}
.page--dense .page-subtitle { max-width: 60rem; }

/* ==========================================================================
   19  FIT-TO-SCREEN PAGE — .page--fit on .app-content, .is-page-fit on <body>
   For a screen that has to be read in one go (Sales Dashboard): the window itself
   never scrolls. The header, KPI strip and tab strip take the height they need and
   .page-fit__panel gets the rest, scrolling inside itself only when a table is
   longer than the window. Pair it with .page--dense.
   ========================================================================== */
body.is-page-fit { overflow: hidden; }
body.is-page-fit .app-main { height: 100vh; min-height: 0; }
.page--fit {
  overflow: hidden;
  min-height: 0;
  padding-top: var(--space-sm);
  padding-bottom: var(--space-xs);
  gap: var(--space-xs);
}
.page--fit .page-header { width: 100%; align-items: center; padding-bottom: 0; }
.page-fit__panel {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: var(--space-2xs);  /* room for the scrollbar, so it never sits on a card */
}
/* Label / value pairs run across in one line of columns rather than wrapping to two. */
.page--fit .detail-grid { grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }
.page--fit .date-filter { align-items: center; gap: var(--space-xs); }
.page--fit .date-filter .form-field { min-width: 8.5rem; }

/* A figure on the Profit & Loss statement: monospaced and tabular so the column reads as a
   column, whether or not it is clickable. */
.pnl-amount {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
}

/* --- A finding on the Risks tab -------------------------------------------
   The money at stake reads as a figure, and the thing to do about it as a sentence the
   reader can act on without translating it first. */
.finding__amount {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-md);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--font-feature-tnum);
  color: var(--color-text);
}
.finding__action {
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-secondary);
  border-left: var(--border-width-thick) solid var(--color-primary-border);
  padding-left: var(--space-sm);
}

/* --- .dash-columns : the dashboard's cards in balanced columns ------------
   A stack of cards is mostly empty space on a wide screen and twice as tall as the
   window. Flowing the same cards into columns fits the whole dashboard on one screen.
   A card is never split across a column break. */
.dash-columns {
  columns: 2;
  column-gap: var(--space-xs);
}
.dash-columns > * {
  break-inside: avoid;
  margin-bottom: var(--space-xs);
}
.dash-columns > :last-child { margin-bottom: 0; }
/* A card whose table has too many columns to live in one: it runs the full width. */
.dash-columns__wide { column-span: all; }
/* Two cards sharing the full width (the matrix and the short settlement tables). Below
   the three-column breakpoint there is no longer room for both, so they stack. */
.dash-split { align-items: start; }
@media (max-width: 1699px) { .dash-split > * { grid-column: 1 / -1; } }
@media (min-width: 1700px) { .dash-columns { columns: 3; } }
/* A wall of small charts takes one more column than a wall of tables. */
@media (min-width: 1700px) { .dash-columns--charts { columns: 4; } }
@media (max-width: 1100px)  { .dash-columns { columns: 1; } }


/* ==========================================================================
   20  SIGN-IN SCREEN
   The one screen with no shell, no sidebar and no data — so it is the one
   place the cafe's own identity is allowed to speak at full volume.

     <body class="auth-screen">
       <main class="auth-layout">
         <aside class="auth-brand">   ... the logo, on its own cream  ...
         <section class="auth-panel"> ... greeting + the form         ...

   The whole app now wears the cafe's olive and gold (section 01), so this
   screen no longer has a palette of its own — only the cream the logo is
   printed on, which has to match the image exactly.
   ========================================================================== */

.auth-screen {
  /* The app's own primary IS the brand olive now, so this screen no longer
     redefines it — only the cream the logo sits on is local to it. */
  --color-primary-soft:   var(--brand-cream);
  --color-primary-border: var(--brand-gold-soft);
  --color-focus-tint:     var(--brand-cream);

  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--space-lg);
  /* Two soft pools of light over deep olive: a photographed-wall feel rather
     than a flat slab of colour. */
  background:
    radial-gradient(48rem 32rem at 12% 8%, rgba(196, 152, 89, 0.30), transparent 62%),
    radial-gradient(42rem 30rem at 92% 96%, rgba(76, 106, 60, 0.55), transparent 58%),
    linear-gradient(140deg, var(--brand-olive-deep) 0%, var(--brand-olive) 58%, #56744a 100%);
}

/* The lockup: brand on the left, the form on the right, one shared shadow. */
.auth-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr);
  width: min(56rem, 100%);
  max-height: calc(100vh - var(--space-lg) * 2);
  overflow: hidden;
  background: var(--color-surface);
  border-radius: var(--radius-xl);
  box-shadow: 0 2rem 4rem rgba(33, 30, 26, 0.45);
}

/* --- Left: the logo on its own cream ------------------------------------- */
.auth-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-xl) var(--space-lg);
  text-align: center;
  /* Flat, and exactly the cream the logo itself is printed on, so the picture has no
     edge to see. Any gradient here — however soft — draws a rectangle around it. */
  background: var(--brand-cream);
  border-right: var(--border-width) solid var(--brand-gold-soft);
}
/* The logo is printed on the same cream as the panel, so it sits on it with
   no card, no border and no seam. */
.auth-brand__logo {
  width: min(15rem, 78%);
  height: auto;
  margin-bottom: calc(var(--space-sm) * -1);
  /* Belt and braces: the corners fade out, so even on a screen that renders the two
     creams a shade apart there is no rectangle. */
  mask-image: radial-gradient(closest-side, #000 86%, transparent 100%);
  -webkit-mask-image: radial-gradient(closest-side, #000 86%, transparent 100%);
}
/* A gold hairline with a diamond at its centre — the logo's own flourish,
   repeated once so the panel reads as one piece. */
.auth-brand__rule {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  width: 68%;
}
.auth-brand__rule::before,
.auth-brand__rule::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--brand-gold), transparent);
}
.auth-brand__rule-dot {
  width: 0.375rem;
  height: 0.375rem;
  background: var(--brand-gold);
  transform: rotate(45deg);
}
.auth-brand__title {
  font-size: var(--font-size-label-caps);
  line-height: var(--line-height-label-caps);
  letter-spacing: 0.18em;
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  color: var(--brand-olive);
}
.auth-brand__line {
  max-width: 20rem;
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--brand-olive-soft);
}
/* Three quiet words about what is inside, so the panel is not purely decorative. */
.auth-brand__points {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-xs) var(--space-base);
  margin-top: var(--space-2xs);
}
.auth-brand__point {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--font-size-label-caps);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand-gold);
}

/* --- Right: the form ------------------------------------------------------ */
.auth-panel {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-base);
  padding: var(--space-xl) var(--space-lg);
  min-width: 0;
  overflow-y: auto;
}
.auth-panel__head { text-align: left; }
.auth-greeting {
  font-family: var(--font-serif);
  font-size: var(--font-size-headline-md);
  line-height: 1.2;
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.01em;
  color: var(--color-text);
  /* Both lines start empty and are filled from the device's clock. Reserving their height
     means the form below does not jump when the text arrives — over a network that gap is
     long enough to see. */
  min-height: 1.2em;
}
.auth-date {
  margin-top: var(--space-2xs);
  font-size: var(--font-size-body-sm);
  color: var(--color-text-muted);
  min-height: 1.4em;
}

/* A field with its icon inside the control rather than in a boxed addon —
   the same idea as .search-field, sized for a login. */
.auth-field { position: relative; display: flex; align-items: center; }
.auth-field .form-input { padding-left: 2.5rem; }
.auth-field--reveal .form-input { padding-right: 2.75rem; }
.auth-field__icon {
  position: absolute;
  left: var(--space-md);
  color: var(--color-text-subtle);
  pointer-events: none;
  font-size: 18px;
}
.auth-field__toggle {
  position: absolute;
  right: var(--space-2xs);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  transition: var(--transition-colors);
}
.auth-field__toggle:hover { background: var(--color-surface-sunken); color: var(--color-text); }

/* Remember-me on the left, the caps-lock warning on the right of the same line. */
.auth-options {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  min-height: 1.25rem;
}
.auth-caps {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--font-size-label-caps);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-warning-text);
}

/* What this copy of the app is talking to. Small, but it is the fastest way
   to tell a PC that was never re-installed from one that was. */
.auth-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
  padding-top: var(--space-sm);
  border-top: var(--border-width) solid var(--color-border-subtle);
  font-size: var(--font-size-label-caps);
  color: var(--color-text-subtle);
}
.auth-foot__where {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
}

/* Narrow window (or a laptop in portrait): the brand panel becomes a header
   band and the logo shrinks, rather than the two panels each halving. */
@media (max-width: 52rem) {
  .auth-layout { grid-template-columns: minmax(0, 1fr); }
  .auth-brand {
    padding: var(--space-lg) var(--space-base) var(--space-base);
    border-right: 0;
    border-bottom: var(--border-width) solid var(--brand-gold-soft);
  }
  .auth-brand__logo { width: 9rem; }
  .auth-brand__line, .auth-brand__points { display: none; }
  .auth-panel { padding: var(--space-lg) var(--space-base); }
}
@media (max-height: 40rem) {
  .auth-brand__points, .auth-brand__line { display: none; }
  .auth-brand__logo { width: 10rem; }
}

/* --- The sidebar lockup, once signed in ----------------------------------
   The full logo is a pentagon of fine detail; at 2rem nothing of it would
   survive. The mark therefore shows the gable — roof, window and flower box —
   enlarged out of the same file, which stays legible at this size. */
.app-sidebar__brand-mark--logo {
  background-color: var(--brand-cream);
  background-image: url('../../assets/logo.png');
  background-repeat: no-repeat;
  background-size: 360%;
  background-position: 50% 6%;
  border-color: var(--brand-gold-soft);
}

/* --- A message shown before it is sent ------------------------------------
   The closing summary, the indent's WhatsApp text: what the person is about to
   send, exactly as it will arrive. Monospaced so the alignment they see is the
   alignment that goes out, and wrapped rather than scrolled sideways. */
.message-preview {
  margin: 0;
  padding: var(--space-md);
  background: var(--color-surface-sunken);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono);
  font-size: var(--font-size-body-sm);
  line-height: 1.55;
  color: var(--color-text);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 22rem;
  overflow-y: auto;
}

/* --- .chart--categorical : a breakdown, not a status ----------------------
   The default series colours are semantic — series 3 is the danger red — which
   is right for "sales vs cancellations" and wrong for "rent vs gas vs wages",
   where nothing is an alarm and the reader only needs to tell six things apart.
   This remaps the six series to the house palette: two olives, two golds, a
   slate blue and a stone, all distinguishable and none of them shouting. */
.chart--categorical .chart__series--1 { stroke: var(--brand-olive); fill: var(--brand-olive); color: var(--brand-olive); }
.chart--categorical .chart__series--2 { stroke: var(--brand-gold); fill: var(--brand-gold); color: var(--brand-gold); }
.chart--categorical .chart__series--3 { stroke: #46697c; fill: #46697c; color: #46697c; }
.chart--categorical .chart__series--4 { stroke: #6f8a5c; fill: #6f8a5c; color: #6f8a5c; }
.chart--categorical .chart__series--5 { stroke: #8c6a36; fill: #8c6a36; color: #8c6a36; }
.chart--categorical .chart__series--6 { stroke: #a69d8e; fill: #a69d8e; color: #a69d8e; }
.chart--categorical .chart__series--1 .chart__swatch { background: var(--brand-olive); }
.chart--categorical .chart__series--2 .chart__swatch { background: var(--brand-gold); }
.chart--categorical .chart__series--3 .chart__swatch { background: #46697c; }
.chart--categorical .chart__series--4 .chart__swatch { background: #6f8a5c; }
.chart--categorical .chart__series--5 .chart__swatch { background: #8c6a36; }
.chart--categorical .chart__series--6 .chart__swatch { background: #a69d8e; }

/* --- .check-wall : a short list of tick boxes, laid out to be scanned -----
   "Who ate it", and anything else where a handful of people or options are
   ticked at once. A column would push the save button off the screen and a
   single row would wrap unreadably, so they flow into as many columns as fit
   and the box scrolls once the list is longer than a screenful. */
.check-wall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: var(--space-2xs) var(--space-sm);
  max-height: 14rem;
  overflow-y: auto;
  padding: var(--space-2xs);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.check-wall .form-check--boxed { padding: var(--space-xs) var(--space-sm); }

/* --- .change-list : what a change actually was ----------------------------
   One line per field on the Audit Log — the name, the old value, an arrow, the
   new one. Monospaced values so two numbers of the same length line up under
   each other, and the old one greyed so the eye lands on what it became. */
.change-list {
  display: flex;
  flex-flow: row wrap;
  align-items: baseline;
  gap: var(--space-2xs) var(--space-sm);
  min-width: 0;
}
/* Six one-word changes stacked six deep made a log of 35px rows into one of
   93px rows. They wrap along the line instead, each kept whole. */
.change-line { break-inside: avoid; }
.change-line + .change-line::before {
  content: '';
  align-self: stretch;
  border-left: var(--border-width) solid var(--color-border);
  margin-right: var(--space-2xs);
}
.change-line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-xs);
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
}
.change-line__field {
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}
.change-line__from,
.change-line__to {
  font-family: var(--font-mono);
  font-size: var(--font-size-numeric-sm);
  overflow-wrap: anywhere;
}
.change-line__from  { color: var(--color-text-muted); text-decoration: line-through; text-decoration-thickness: 1px; }
.change-line__arrow { color: var(--color-text-subtle); }
.change-line__to    { color: var(--color-text); font-weight: var(--font-weight-semibold); }

/* --- .filter-bar : the filter controls as one line, not a panel -----------
   A screen whose filters sat in their own titled card with the button on a row
   of its own spent 230px — a quarter of the window — before showing a single
   row of what it was filtering. The controls are short and there is width going
   spare, so they run along one line and the button sits at the end of it. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-sm) var(--space-md);
}
.filter-bar .form-field { min-width: 9rem; flex: 0 1 12rem; }
.filter-bar__spacer { flex: 1 1 auto; }

/* --- .note-fold : a long explanation that stays folded until wanted -------
   Some warnings have to be read once and never again — how a 20 m fence behaves
   on a desktop, say. Left open they are three paragraphs of a settings screen
   nobody rereads; deleted they are a support call. Folded, they are a line. */
.note-fold {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
}
.note-fold > summary {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  cursor: pointer;
  list-style: none;
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}
.note-fold > summary::-webkit-details-marker { display: none; }
.note-fold > summary::after {
  content: 'expand_more';
  font-family: 'Material Symbols Outlined';
  font-size: 18px;
  margin-left: auto;
  color: var(--color-text-subtle);
  transition: transform var(--duration-base) var(--easing-standard);
}
.note-fold[open] > summary::after { transform: rotate(180deg); }
.note-fold__body {
  padding: 0 var(--space-md) var(--space-md);
  font-size: var(--font-size-body-sm);
  line-height: var(--line-height-body-sm);
  color: var(--color-text-secondary);
}

/* --- .page--tight : a title block that does not cost 126px ----------------
   The page header's bottom padding and the subtitle's own line make sense on a
   dashboard. On a short working screen they push the work below the fold. */
.page--tight .page-header { padding-bottom: var(--space-sm); }
.page--tight { gap: var(--space-md); padding-top: var(--space-md); }
/* One line of description rather than two: the 44rem measure is for a paragraph,
   and these are single sentences. */
.page--tight .page-subtitle { max-width: 62rem; }

/* --- .cell-when : a timestamp that stays on one line ----------------------
   A date and a time wrapping onto two lines makes every row in a log a third
   taller than it needs to be, and the column it would have needed was going
   spare anyway. */
.cell-when { white-space: nowrap; }
