/* ==========================================================================
   DesiMenus — design tokens
   Generated from desimenus-tokens.json. Do not hand-edit.

   Consumers: DesiMenus.Web.Admin, DesiMenus.SaaSAdmin, desimenus-customer-kiosk

   Usage:
     <html data-theme="light|dark|kitchen|sunlight"
           data-density="compact|comfortable|kitchen"
           data-expression="service|appetite">

   Component CSS references SEMANTIC tokens (--ds-state-overdue), never ramp
   steps (--ds-mirchi-500). Ramp steps exist so semantic tokens have something
   to point at.
   ========================================================================== */

:root {
  /* ---- Ramps ------------------------------------------------------------ */
  --ds-primary-50:  #EEF6FD;  --ds-primary-100: #D6EAFA;  --ds-primary-200: #ADD5F4;
  --ds-primary-300: #7DBBEB;  --ds-primary-400: #3697DB;  --ds-primary-500: #1E7CC4;
  --ds-primary-600: #1463A4;  --ds-primary-700: #0F4E83;  --ds-primary-800: #0C3C64;
  --ds-primary-900: #082A47;

  --ds-slate-0:    #FFFFFF;  --ds-slate-25:   #F7F9FB;  --ds-slate-50:   #EFF3F7;
  --ds-slate-100:  #E2E8EF;  --ds-slate-200:  #CBD4DF;  --ds-slate-300:  #A7B4C4;
  --ds-slate-400:  #7A8899;  --ds-slate-500:  #5A6878;  --ds-slate-600:  #43505F;
  --ds-slate-700:  #303B48;  --ds-slate-800:  #1E2833;  --ds-slate-900:  #131C25;
  --ds-slate-950:  #0B1219;  --ds-slate-1000: #05090D;

  --ds-pudina-50: #E6F5EF; --ds-pudina-200: #A8DCC5; --ds-pudina-300: #5FC49B;
  --ds-pudina-400: #2E9E75; --ds-pudina-500: #16875E; --ds-pudina-600: #0E6E4C;
  --ds-pudina-700: #0A563B;

  --ds-haldi-50: #FBF1DC; --ds-haldi-200: #EBCE8E; --ds-haldi-300: #F0C36A;
  --ds-haldi-400: #E0A32E; --ds-haldi-500: #C4841A; --ds-haldi-600: #9E6710;
  --ds-haldi-700: #7A4F0C;

  --ds-mirchi-50: #FCEBEC; --ds-mirchi-200: #F0B4BA; --ds-mirchi-300: #F08A8A;
  --ds-mirchi-400: #E66A6A; --ds-mirchi-500: #C42D3C; --ds-mirchi-600: #9E1F2E;
  --ds-mirchi-700: #7A1723;

  /* ── TANDOOR — the InKitchen state, and ONLY that.
     The extra 100/800/900 steps date from a draft that made this the
     console accent; that draft was wrong (see --ds-action) and the steps
     are kept only because the empty-state glyph uses 300. Nothing here is
     a brand value. */
  --ds-tandoor-50: #FCEEE6; --ds-tandoor-100: #F9DCCB; --ds-tandoor-200: #F3BE9C;
  --ds-tandoor-300: #EE8F5E; --ds-tandoor-400: #E2622B; --ds-tandoor-500: #C24A18;
  --ds-tandoor-600: #9B3A11; --ds-tandoor-700: #762C0D; --ds-tandoor-800: #4A2413;
  --ds-tandoor-900: #341A0E;

  /* ── SKY — the brand hue diluted to a surface ramp ─────────────────
     Not a second brand colour and not a neutral: primary at 4-12%, for the
     PAPER the numbers sit on. Canvas, sunken wells, hairlines.

     ⚠ THIS DOES NOT REPLACE SLATE AND MUST NOT. Slate stays the text and
     structural ramp — the numbers in this product are read all day by the
     people whose money they are, and tinted greys go muddy at small sizes.
     slate-900 on sky-25 measures 16:1, the same figure it made on
     slate-25: the mood changes and the legibility does not. */
  --ds-sky-0:   #FFFFFF;  --ds-sky-25:  #F5F8FC;  --ds-sky-50:  #ECF2F9;
  --ds-sky-100: #DCE7F2;  --ds-sky-200: #C3D5E8;  --ds-sky-300: #9DB8D4;

  /* ── NEEL — the platform console's blue ────────────────────────────
     "Neel" is indigo, the dye this subcontinent exported for three
     centuries. Chosen over a generic blue because the tenant console
     already owns azure (--ds-primary), and §13.2 requires the two
     consoles to be tellable apart AT A GLANCE: a support agent with both
     open needs to know which one they are about to click "suspend
     tenant" in.

     Indigo vs azure is a real distinction at a glance while still
     reading, unmistakably, as blue. The rail below reinforces it with a
     dark panel — a much louder signal than an accent tint alone. */
  --ds-neel-50:  #EEF4FE;  --ds-neel-100: #D8E6FC;  --ds-neel-200: #B4CEF8;
  --ds-neel-300: #82ADF2;  --ds-neel-400: #4F86E6;  --ds-neel-500: #2C63CE;
  --ds-neel-600: #1D4CA8;  --ds-neel-700: #173C85;  --ds-neel-800: #142F63;
  --ds-neel-900: #0F2447;  --ds-neel-950: #091830;

  --ds-jamun-50: #F0EBFB; --ds-jamun-200: #C7B4EC; --ds-jamun-300: #B39CE8;
  --ds-jamun-400: #9B7FE0; --ds-jamun-500: #6F4FBF; --ds-jamun-600: #573C9B;
  --ds-jamun-700: #432E78;

  /* ---- Type ------------------------------------------------------------- */
  --ds-font-ui:      "IBM Plex Sans", "Segoe UI Variable", -apple-system, Roboto, system-ui, sans-serif;
  /* GX-7 — ⚠ "Kohinoor Devanagari" ADDED, AND IT IS THE ENTRY THAT MAKES THIS
     STACK WORK ON HALF THE PHONES THAT WILL SEE IT.

     The stack named IBM Plex Sans Devanagari (which nothing installs) and Noto
     Sans Devanagari (which Android ships) and then fell through to generic
     sans-serif. On iOS that meant the browser's own script fallback rather
     than a chosen face — usually legible, occasionally not, and never the one
     anybody intended. Kohinoor Devanagari is what iOS actually ships.

     ⚠ AND NO @font-face ANYWHERE. Vendoring a Devanagari webfont is 200-400KB
     — the customer kiosk's entire CSS+JS weight budget — to replace a system
     face that both platforms already have. A hungry person on a restaurant's
     Wi-Fi waiting on a font download before the menu paints is a worse outcome
     than slightly different letterforms. See the GX-7 notes, which record this
     as a reversal of an earlier intention rather than an omission. */
  --ds-font-indic:   "IBM Plex Sans Devanagari", "Noto Sans Devanagari", "Kohinoor Devanagari", "Nirmala UI", sans-serif;
  --ds-font-numeric: "IBM Plex Mono", SFMono-Regular, Consolas, monospace;
  --ds-font-display: var(--ds-font-ui);
  --ds-numeric-features: "tnum" 1, "zero" 1;

  /* ---- Space ------------------------------------------------------------ */
  --ds-space-0: 0px;  --ds-space-px: 2px; --ds-space-1: 4px;  --ds-space-2: 8px;
  --ds-space-3: 12px; --ds-space-4: 16px; --ds-space-5: 20px; --ds-space-6: 24px;
  --ds-space-8: 32px; --ds-space-10: 40px; --ds-space-12: 48px; --ds-space-16: 64px;

  /* ---- Shape ------------------------------------------------------------ */
  --ds-radius-none: 0px;  --ds-radius-xs: 4px;  --ds-radius-sm: 6px;
  --ds-radius-md: 10px;   --ds-radius-lg: 14px; --ds-radius-xl: 20px;
  --ds-radius-pill: 999px;
  --ds-radius-card: var(--ds-radius-md);
  --ds-radius-railed-card: 0 var(--ds-radius-card) var(--ds-radius-card) 0;

  /* ---- Motion ----------------------------------------------------------- */
  --ds-motion-instant: 0ms;
  --ds-motion-fast: 120ms;
  --ds-motion-base: 180ms;
  --ds-motion-slow: 260ms;
  --ds-motion-pulse: 3000ms;
  --ds-ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
  --ds-ease-exit: cubic-bezier(0.2, 0, 0, 1);

  /* ---- Focus ------------------------------------------------------------ */
  --ds-focus-width: 2px;
  --ds-focus-offset: 2px;
}

/* ==========================================================================
   Density — set on <html> or on any region. Density is per REGION, not per app:
   a POS on a touch monitor is comfortable with a compact order-list region.
   ========================================================================== */

:root,
[data-density="comfortable"] {
  --ds-row-h: 48px;    --ds-min-target: 48px; --ds-btn-h: 48px;
  --ds-icon: 20px;     --ds-rail-w: 6px;
  --ds-pad-x: 16px;    --ds-pad-y: 12px;
  --ds-chip-h: 28px;

  --ds-display:    600 28px/34px var(--ds-font-ui);
  --ds-title-lg:   600 22px/28px var(--ds-font-ui);
  --ds-title:      600 18px/24px var(--ds-font-ui);
  --ds-body-lg:    400 17px/24px var(--ds-font-ui);
  --ds-body:       400 15px/22px var(--ds-font-ui);
  --ds-label:      500 13px/18px var(--ds-font-ui);
  --ds-caption:    400 12px/16px var(--ds-font-ui);
  --ds-overline:   600 11px/14px var(--ds-font-ui);
  --ds-numeric-xl: 500 28px/32px var(--ds-font-numeric);
  --ds-numeric:    400 17px/24px var(--ds-font-numeric);
  --ds-numeric-sm: 400 13px/18px var(--ds-font-numeric);
}

[data-density="compact"] {
  --ds-row-h: 36px;    --ds-min-target: 32px; --ds-btn-h: 34px;
  --ds-icon: 16px;     --ds-rail-w: 4px;
  --ds-pad-x: 12px;    --ds-pad-y: 8px;
  --ds-chip-h: 24px;

  --ds-display:    600 24px/30px var(--ds-font-ui);
  --ds-title-lg:   600 19px/25px var(--ds-font-ui);
  --ds-title:      600 16px/22px var(--ds-font-ui);
  --ds-body-lg:    400 15px/22px var(--ds-font-ui);
  --ds-body:       400 13px/19px var(--ds-font-ui);
  --ds-label:      500 12px/16px var(--ds-font-ui);
  --ds-caption:    400 11px/15px var(--ds-font-ui);
  --ds-overline:   600 10px/14px var(--ds-font-ui);
  --ds-numeric-xl: 500 24px/28px var(--ds-font-numeric);
  --ds-numeric:    400 15px/20px var(--ds-font-numeric);
  --ds-numeric-sm: 400 12px/16px var(--ds-font-numeric);
}

[data-density="kitchen"] {
  --ds-row-h: 76px;    --ds-min-target: 88px; --ds-btn-h: 88px;
  --ds-icon: 32px;     --ds-rail-w: 10px;
  --ds-pad-x: 24px;    --ds-pad-y: 20px;
  --ds-chip-h: 44px;

  --ds-display:    600 40px/46px var(--ds-font-ui);
  --ds-title-lg:   600 30px/36px var(--ds-font-ui);
  --ds-title:      600 24px/30px var(--ds-font-ui);
  --ds-body-lg:    400 22px/30px var(--ds-font-ui);
  --ds-body:       400 20px/28px var(--ds-font-ui);   /* KDS item-line floor */
  --ds-label:      500 18px/24px var(--ds-font-ui);   /* KDS ticket-font floor */
  --ds-caption:    400 16px/22px var(--ds-font-ui);
  --ds-overline:   600 14px/18px var(--ds-font-ui);
  --ds-numeric-xl: 500 40px/44px var(--ds-font-numeric);
  --ds-numeric:    400 22px/28px var(--ds-font-numeric); /* KDS timer floor */
  --ds-numeric-sm: 400 18px/22px var(--ds-font-numeric);
}

/* ==========================================================================
   Themes
   ========================================================================== */

:root,
[data-theme="light"] {
  color-scheme: light;

  /* ⚠ THE CANVAS IS TINTED AND THE SURFACE IS NOT. Cards stay pure white so
     they lift off the page; the page carries a wash of the brand blue. That
     contrast — white on tint — is what gives the console depth without one
     extra border or shadow, and it is where most of the "liveliness" in this
     theme actually comes from. Tinting both would flatten it again.

     ⚠ THE TINT IS THE BRAND HUE, NOT A WARM ONE. A cream page under a blue
     brand reads as two design systems sharing a screen. sky-25 is the same
     blue as the logo, diluted until slate-900 still measures 16:1 on it —
     the identical figure it made on the old neutral canvas. */
  --ds-canvas: var(--ds-sky-25);
  --ds-surface: var(--ds-slate-0);
  --ds-surface-raised: var(--ds-slate-0);
  --ds-surface-sunken: var(--ds-sky-50);
  --ds-text: var(--ds-slate-900);
  --ds-text-secondary: var(--ds-slate-500);
  --ds-text-tertiary: var(--ds-slate-400);
  --ds-text-on-action: var(--ds-slate-0);
  --ds-border: var(--ds-sky-100);
  --ds-border-strong: var(--ds-sky-200);

  /* ⚠ THE ACTION COLOUR IS THE BRAND, AND THE SPLIT BETWEEN 400 AND 600 IS
     THE WHOLE OF §3. The logo blue is primary-400 #3697DB, and it measures
     3.15:1 on white — above the 3:1 floor for large graphics and below the
     4.5:1 body-text minimum. So it is the BRAND value, not the action
     value: it illuminates dark surfaces, marks the rail, and draws the mark
     itself. Actions take primary-600 #1463A4 (6.27:1 with white on it) and
     links take 700 (8.09:1), because a link sits inside a paragraph and has
     to win against body text where a button has a filled field to help it.

     ⚠ AN EARLIER DRAFT MOVED THIS TO TANDOOR TO "WARM UP" THE CONSOLE. It
     warmed it and it also unbranded it, and it put the accent on a hue the
     order state machine already owns for InKitchen. What warms this theme
     is the canvas, the rail depth and the empty states — never the brand. */
  --ds-action: var(--ds-primary-600);
  --ds-action-hover: var(--ds-primary-500);
  --ds-action-pressed: var(--ds-primary-700);
  --ds-action-subtle: var(--ds-primary-50);
  --ds-link: var(--ds-primary-700);
  --ds-focus: var(--ds-primary-600);
  --ds-focus-inner: var(--ds-slate-0);
  --ds-brand: var(--ds-primary-400);
  --ds-scrim: rgba(8, 42, 71, 0.56);

  /* Shadows tinted warm rather than neutral-grey. A grey shadow under a
     warm-tinted page reads as dirt on the paper; three degrees of hue on
     the shadow is the difference between "shadow" and "smudge". */
  --ds-elev-1: 0 1px 2px rgba(8,42,71,0.07), 0 1px 3px rgba(8,42,71,0.06);
  --ds-elev-2: 0 4px 14px rgba(8,42,71,0.11), 0 2px 4px rgba(8,42,71,0.06);

  /* ── THE TENANT RAIL ───────────────────────────────────────────────
     A deep counter-blue panel — the brand hue at panel depth, carrying the
     DesiMenus mark and wordmark.

     ⚠ §13.2 STILL HOLDS, AND IT IS TIGHTER HERE THAN IT LOOKS. Both rails
     are now dark blues: counter blue #123A5E here, slate-indigo #1C3054 on
     the SaaS console. They are told apart by the LOCKUP — "DesiMenus / Web
     Admin" against "DesiMenus / SaaS Admin" — and by the accent, azure
     against neel, which the rest of each console repeats on every button.
     That is a smaller gap than orange-against-navy would have been, and it
     is the correct one: a product does not get two brand colours so that
     its own staff can navigate.

     ⚠ AND IT IS DELIBERATELY NOT NEAR-BLACK, for the reason the SaaS rail
     records: #123A5E sits 11.7:1 from the work surface, in the same band
     as the indigo rail's 13.1:1, so the edge between rail and content does
     not burn on a glance across it. */
  --ds-rail-bg: #123A5E;
  --ds-rail-bg-deep: #0C2B46;
  --ds-rail-fg: #E8F1FA;
  --ds-rail-fg-muted: #A9C4DE;
  --ds-rail-hover: rgba(255, 255, 255, 0.08);
  --ds-rail-active: rgba(255, 255, 255, 0.13);
  /* ⚠ THE MARKER IS THE LOGO VALUE ITSELF, primary-400. §3 reserves
     #3697DB for exactly this — "accents on dark surfaces" — and at 3.69:1
     against the rail it clears the 3:1 graphics floor while staying too
     light for rail TEXT, which is why the labels use rail-fg instead. */
  --ds-rail-marker: var(--ds-primary-400);
  --ds-rail-border: rgba(255, 255, 255, 0.10);

  /* ── METRIC ACCENTS ────────────────────────────────────────────────
     Four hues the dashboard tiles rotate through, in a fixed order so the
     same figure keeps the same colour on every visit.

     ⚠ FOUR STEPS OF ONE HUE, NOT FOUR HUES, AND THE REASON IS §2.1.
     A first draft rotated pudina, haldi and jamun through these tiles and
     it looked livelier and it was wrong: those hues carry state — served,
     awaiting, refunded — so a green rule over a sales figure is a status
     claim about that figure, made by a stylesheet, in a product whose own
     principle is that colour is never the message. Stepping through the
     brand ramp separates the tiles without saying anything at all.

     They appear only as a short rule above a metric label, never as a
     fill, never on a chip. A tile that has something to say drops the
     accent and wears --ds-state-* instead. */
  --ds-metric-a: var(--ds-primary-600);
  --ds-metric-b: var(--ds-primary-400);
  --ds-metric-c: var(--ds-primary-800);
  --ds-metric-d: var(--ds-primary-300);

  --ds-state-open: var(--ds-slate-500);            --ds-state-open-fill: var(--ds-slate-50);
  --ds-state-in-kitchen: var(--ds-tandoor-500);    --ds-state-in-kitchen-fill: var(--ds-tandoor-50);
  --ds-state-served: var(--ds-pudina-600);         --ds-state-served-fill: var(--ds-pudina-50);
  --ds-state-billed: var(--ds-primary-600);        --ds-state-billed-fill: var(--ds-primary-50);
  --ds-state-cancelled: var(--ds-slate-400);       --ds-state-cancelled-fill: transparent;
  --ds-state-pending: var(--ds-haldi-600);         --ds-state-pending-fill: var(--ds-haldi-50);
  --ds-state-settled: var(--ds-pudina-600);        --ds-state-settled-fill: var(--ds-pudina-50);
  --ds-state-failed: var(--ds-mirchi-500);         --ds-state-failed-fill: var(--ds-mirchi-50);
  --ds-state-refunded: var(--ds-jamun-500);        --ds-state-refunded-fill: var(--ds-jamun-50);
  --ds-state-void: var(--ds-slate-400);            --ds-state-void-fill: transparent;
  --ds-state-locked: var(--ds-haldi-600);          --ds-state-locked-fill: var(--ds-haldi-50);

  --ds-sync-ok: var(--ds-pudina-600);
  --ds-sync-queued: var(--ds-haldi-600);
  --ds-sync-failed: var(--ds-mirchi-500);
  --ds-sync-offline: var(--ds-slate-600);
}

[data-theme="dark"] {
  color-scheme: dark;
  --ds-canvas: var(--ds-slate-950);
  --ds-surface: var(--ds-slate-900);
  --ds-surface-raised: var(--ds-slate-800);
  --ds-surface-sunken: var(--ds-slate-1000);
  --ds-text: var(--ds-slate-200);
  --ds-text-secondary: var(--ds-slate-300);
  --ds-text-tertiary: var(--ds-slate-400);
  --ds-text-on-action: var(--ds-slate-950);
  --ds-border: var(--ds-slate-700);
  --ds-border-strong: var(--ds-slate-600);
  /* On a dark field the brand value finally gets to BE the action colour:
     §3 keeps primary-400 off light-theme actions because 3.15:1 on white
     cannot carry a label, and against slate-900 the same value measures
     6.6:1. Same hue as the logo, doing real work. */
  --ds-action: var(--ds-primary-400);
  --ds-action-hover: var(--ds-primary-300);
  --ds-action-pressed: var(--ds-primary-500);
  --ds-action-subtle: rgba(54, 151, 219, 0.14);
  --ds-link: var(--ds-primary-300);
  --ds-focus: var(--ds-primary-400);
  --ds-focus-inner: var(--ds-slate-950);
  --ds-brand: var(--ds-primary-400);

  /* In a dark theme the rail is no longer the loud element — the whole
     screen is dark — so it sits one step deeper than the canvas rather
     than becoming a coloured panel, and the marker carries the hue. */
  --ds-rail-bg: var(--ds-slate-950);
  --ds-rail-bg-deep: var(--ds-slate-1000);
  --ds-rail-fg: var(--ds-slate-50);
  --ds-rail-fg-muted: var(--ds-slate-300);
  --ds-rail-hover: rgba(255, 255, 255, 0.07);
  --ds-rail-active: rgba(255, 255, 255, 0.11);
  --ds-rail-marker: var(--ds-primary-400);
  --ds-rail-border: rgba(255, 255, 255, 0.09);

  --ds-metric-a: var(--ds-primary-400);
  --ds-metric-b: var(--ds-primary-300);
  --ds-metric-c: var(--ds-primary-200);
  --ds-metric-d: var(--ds-primary-500);
  --ds-scrim: rgba(5, 9, 13, 0.72);
  /* Dark expresses elevation through surface value + hairline, never shadow. */
  --ds-elev-1: none;
  --ds-elev-2: none;

  --ds-state-open: var(--ds-slate-300);            --ds-state-open-fill: rgba(203,212,223,0.10);
  --ds-state-in-kitchen: var(--ds-tandoor-400);    --ds-state-in-kitchen-fill: rgba(226,98,43,0.14);
  --ds-state-served: var(--ds-pudina-400);         --ds-state-served-fill: rgba(46,158,117,0.14);
  --ds-state-billed: var(--ds-primary-400);        --ds-state-billed-fill: rgba(54,151,219,0.14);
  --ds-state-cancelled: var(--ds-slate-400);       --ds-state-cancelled-fill: transparent;
  --ds-state-pending: var(--ds-haldi-400);         --ds-state-pending-fill: rgba(224,163,46,0.14);
  --ds-state-settled: var(--ds-pudina-400);        --ds-state-settled-fill: rgba(46,158,117,0.14);
  --ds-state-failed: var(--ds-mirchi-400);         --ds-state-failed-fill: rgba(230,106,106,0.14);
  --ds-state-refunded: var(--ds-jamun-400);        --ds-state-refunded-fill: rgba(155,127,224,0.14);
  --ds-state-void: var(--ds-slate-400);            --ds-state-void-fill: transparent;
  --ds-state-locked: var(--ds-haldi-400);          --ds-state-locked-fill: rgba(224,163,46,0.14);

  --ds-sync-ok: var(--ds-pudina-400);
  --ds-sync-queued: var(--ds-haldi-400);
  --ds-sync-failed: var(--ds-mirchi-400);
  --ds-sync-offline: var(--ds-slate-300);
}

/* KDS. Not "dark mode, bigger" — flat by rule, boosted hues, read at 2 m. */
[data-theme="kitchen"] {
  color-scheme: dark;
  --ds-canvas: var(--ds-slate-1000);
  --ds-surface: #0F161D;
  --ds-surface-raised: #18222C;
  --ds-surface-sunken: var(--ds-slate-1000);
  --ds-text: #F2F6FA;
  --ds-text-secondary: var(--ds-slate-200);
  --ds-text-tertiary: var(--ds-slate-300);
  --ds-text-on-action: var(--ds-slate-1000);
  --ds-border: var(--ds-slate-600);
  --ds-border-strong: var(--ds-slate-500);
  /* ⚠ KITCHEN KEEPS ITS OWN LOGIC. Read at arm's length in a hot, bright
     room, so it optimises for luminance over everything: the accent goes
     one step lighter again, to primary-300. */
  --ds-action: var(--ds-primary-300);
  --ds-action-hover: var(--ds-primary-200);
  --ds-action-pressed: var(--ds-primary-400);
  --ds-action-subtle: rgba(125, 187, 235, 0.12);
  --ds-link: var(--ds-primary-300);
  --ds-focus: var(--ds-primary-300);
  --ds-focus-inner: var(--ds-slate-1000);
  --ds-brand: var(--ds-primary-400);

  --ds-rail-bg: #0B1219;
  --ds-rail-bg-deep: var(--ds-slate-1000);
  --ds-rail-fg: #FFFFFF;
  --ds-rail-fg-muted: var(--ds-slate-200);
  --ds-rail-hover: rgba(255, 255, 255, 0.10);
  --ds-rail-active: rgba(255, 255, 255, 0.16);
  --ds-rail-marker: var(--ds-primary-300);
  --ds-rail-border: rgba(255, 255, 255, 0.12);

  --ds-metric-a: var(--ds-primary-300);
  --ds-metric-b: var(--ds-primary-200);
  --ds-metric-c: var(--ds-primary-400);
  --ds-metric-d: var(--ds-primary-100);
  --ds-scrim: rgba(5, 9, 13, 0.82);
  --ds-elev-1: none;
  --ds-elev-2: none;

  --ds-urgency-normal: var(--ds-slate-200);   --ds-ticket-normal: #0F161D;
  --ds-urgency-warning: var(--ds-haldi-300);  --ds-ticket-warning: #1F1705;
  --ds-urgency-overdue: var(--ds-mirchi-300); --ds-ticket-overdue: #230A0C;
  --ds-urgency-ready: var(--ds-pudina-300);   --ds-ticket-ready: #04170F;
  --ds-ticket-void: #2A0C0F;
}

/* Rider outdoors. Weight increases; nothing moves. */
[data-theme="sunlight"] {
  color-scheme: light;
  --ds-canvas: var(--ds-slate-0);
  --ds-surface: var(--ds-slate-0);
  --ds-surface-raised: var(--ds-slate-0);
  --ds-surface-sunken: var(--ds-slate-50);
  --ds-text: var(--ds-slate-950);
  --ds-text-secondary: var(--ds-slate-600);
  --ds-text-tertiary: var(--ds-slate-500);
  --ds-text-on-action: var(--ds-slate-0);
  --ds-border: var(--ds-slate-400);
  --ds-border-strong: var(--ds-slate-500);
  /* ⚠ SUNLIGHT STAYS ON PURE WHITE AND KEEPS ITS HARD BORDERS. This theme
     exists for a tablet on a terrace at noon, where even the sky-25 canvas
     would cost contrast the theme was created to buy. It takes the darkest
     usable step of the brand ramp and nothing else from this pass. */
  --ds-action: var(--ds-primary-700);
  --ds-action-hover: var(--ds-primary-600);
  --ds-action-pressed: var(--ds-primary-800);
  --ds-action-subtle: var(--ds-primary-100);
  --ds-link: var(--ds-primary-700);
  --ds-focus: var(--ds-primary-700);
  --ds-focus-inner: var(--ds-slate-0);
  --ds-brand: var(--ds-primary-600);

  --ds-rail-bg: var(--ds-primary-900);
  --ds-rail-bg-deep: #061D33;
  --ds-rail-fg: #FFFFFF;
  --ds-rail-fg-muted: #CFE2F3;
  --ds-rail-hover: rgba(255, 255, 255, 0.12);
  --ds-rail-active: rgba(255, 255, 255, 0.18);
  --ds-rail-marker: var(--ds-primary-200);
  --ds-rail-border: rgba(255, 255, 255, 0.16);

  --ds-metric-a: var(--ds-primary-800);
  --ds-metric-b: var(--ds-primary-600);
  --ds-metric-c: var(--ds-primary-900);
  --ds-metric-d: var(--ds-primary-700);
  --ds-elev-1: 0 1px 2px rgba(19,28,37,0.16);
  --ds-elev-2: 0 4px 12px rgba(19,28,37,0.20);
}

/* Sunlight chips go solid-fill with white text. */
[data-theme="sunlight"] .ds-chip {
  color: var(--ds-slate-0);
  border-color: transparent;
  background: var(--ds-chip-color, var(--ds-slate-600));
}

/* SaaS Admin differs from Web Admin by exactly one token. A support agent
   should never be unsure which console they are in. */
[data-console="saas"] { --ds-action: var(--ds-jamun-600); --ds-link: var(--ds-jamun-600); --ds-focus: var(--ds-jamun-600); }

/* Appetite expression — kiosk PWA and customer display only. */
[data-expression="appetite"] {
  --ds-font-display: "IBM Plex Serif", Georgia, serif;
  --ds-radius-card: var(--ds-radius-lg);
  --ds-pad-x: 20px;
  --ds-pad-y: 16px;
}

/* ==========================================================================
   Base
   ========================================================================== */

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

body {
  margin: 0;
  background: var(--ds-canvas);
  color: var(--ds-text);
  font: var(--ds-body);
  font-family: var(--ds-font-ui), var(--ds-font-indic);
  -webkit-font-smoothing: antialiased;
}

/* Every numeral, everywhere. Tabular figures and a slashed zero, so a column
   of amounts aligns on the decimal without a fixed-width hack. */
.ds-numeric, .ds-money, .ds-timer, .ds-id,
input[type="number"], td.ds-num, th.ds-num {
  font: var(--ds-numeric);
  font-feature-settings: var(--ds-numeric-features);
  font-variant-numeric: tabular-nums slashed-zero;
}

/* Amounts and identifiers never wrap, never truncate, always selectable. */
.ds-money, .ds-id {
  white-space: nowrap;
  user-select: text;
  text-align: right;
  min-width: 12ch;
}

.ds-money--struck { text-decoration: line-through; color: var(--ds-text-tertiary); }

/* ---- Type roles -------------------------------------------------------- */
.ds-display  { font: var(--ds-display);  letter-spacing: -0.02em; }
.ds-title-lg { font: var(--ds-title-lg); letter-spacing: -0.01em; }
.ds-title    { font: var(--ds-title); }
.ds-body-lg  { font: var(--ds-body-lg); }
.ds-body     { font: var(--ds-body); }
.ds-label    { font: var(--ds-label);   letter-spacing: 0.005em; }
.ds-caption  { font: var(--ds-caption); letter-spacing: 0.01em; color: var(--ds-text-secondary); }
.ds-overline { font: var(--ds-overline); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ds-text-secondary); }

/* ---- Focus. Never removed, only restyled. ------------------------------ */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--ds-focus-width) solid var(--ds-focus);
  outline-offset: var(--ds-focus-offset);
  box-shadow: 0 0 0 1px var(--ds-focus-inner);
  border-radius: var(--ds-radius-xs);
}

/* ==========================================================================
   The rail — signature element.
   Four channels: hue, pattern, icon, label. Survives greyscale, thermal
   print, and colour blindness.
   ========================================================================== */

.ds-railed {
  position: relative;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-left: none;
  border-radius: var(--ds-radius-railed-card);
  padding: var(--ds-pad-y) var(--ds-pad-x);
  padding-left: calc(var(--ds-pad-x) + var(--ds-rail-w));
  box-shadow: var(--ds-elev-1);
}

.ds-railed::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--ds-rail-w);
  background: var(--ds-rail-color, var(--ds-slate-300));
}

/* Solid — resolved, no action pending. (default, no modifier) */

/* Dashed — in progress, time is passing. */
.ds-rail--dashed::before {
  background: repeating-linear-gradient(
    to bottom,
    var(--ds-rail-color) 0 8px,
    transparent 8px 14px
  );
}
[data-density="kitchen"] .ds-rail--dashed::before {
  background: repeating-linear-gradient(
    to bottom,
    var(--ds-rail-color) 0 14px,
    transparent 14px 24px
  );
}

/* Double — blocked; a person must decide. */
.ds-rail--double::before {
  background: linear-gradient(
    to right,
    var(--ds-rail-color) 0 2px,
    transparent 2px 4px,
    var(--ds-rail-color) 4px 6px,
    transparent 6px
  );
}

/* Hollow — void or cancelled. Amount is struck through by the row. */
.ds-rail--hollow::before {
  background: transparent;
  border: 1.5px solid var(--ds-rail-color);
  border-right: none;
}

/* State bindings. Set --ds-rail-color; the pattern class does the rest. */
.ds-state-open        { --ds-rail-color: var(--ds-state-open); }
.ds-state-in-kitchen  { --ds-rail-color: var(--ds-state-in-kitchen); }
.ds-state-served      { --ds-rail-color: var(--ds-state-served); }
.ds-state-billed      { --ds-rail-color: var(--ds-state-billed); }
.ds-state-cancelled   { --ds-rail-color: var(--ds-state-cancelled); }
.ds-state-pending     { --ds-rail-color: var(--ds-state-pending); }
.ds-state-settled     { --ds-rail-color: var(--ds-state-settled); }
.ds-state-failed      { --ds-rail-color: var(--ds-state-failed); }
.ds-state-refunded    { --ds-rail-color: var(--ds-state-refunded); }
.ds-state-void        { --ds-rail-color: var(--ds-state-void); }
.ds-state-locked      { --ds-rail-color: var(--ds-state-locked); }

/* ==========================================================================
   Components
   ========================================================================== */

/* ---- Buttons ----------------------------------------------------------- */
.ds-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--ds-space-2);
  min-height: var(--ds-btn-h);
  min-width: var(--ds-min-target);
  padding: 0 var(--ds-pad-x);
  border: 1px solid transparent;
  border-radius: var(--ds-radius-sm);
  font: var(--ds-label);
  cursor: pointer;
  transition: background var(--ds-motion-fast) var(--ds-ease-standard),
              border-color var(--ds-motion-fast) var(--ds-ease-standard);
}
.ds-btn--primary     { background: var(--ds-action); color: var(--ds-text-on-action); }
.ds-btn--primary:hover  { background: var(--ds-action-hover); }
.ds-btn--primary:active { background: var(--ds-action-pressed); }
.ds-btn--secondary   { background: transparent; color: var(--ds-link); border-color: var(--ds-border-strong); }
.ds-btn--secondary:hover { background: var(--ds-action-subtle); }
.ds-btn--ghost       { background: transparent; color: var(--ds-text); }
.ds-btn--ghost:hover { background: var(--ds-surface-sunken); }
.ds-btn--destructive { background: var(--ds-mirchi-600); color: var(--ds-slate-0); }

/* Not possible right now. Faint. */
.ds-btn:disabled,
.ds-btn--disabled {
  background: var(--ds-surface-sunken);
  color: var(--ds-text-tertiary);
  border-color: transparent;
  cursor: not-allowed;
}

/* Possible, but not by you. Bright, tappable, opens AUTH-11.
   The difference from disabled is deliberate: a waiter should be able to spot
   every locked control at a glance, because spotting them is how they know to
   call a manager. */
.ds-btn--locked {
  background: var(--ds-state-locked-fill);
  color: var(--ds-state-locked);
  border: 1.5px double var(--ds-state-locked);
  border-width: 3px;
  border-style: double;
  cursor: pointer;
}

/* POS action buttons on a touch monitor. */
.ds-btn--pos { min-height: 56px; min-width: 56px; font: var(--ds-body-lg); font-weight: 600; }

/* Busy holds a minimum 300 ms and preserves width so layout never jumps. */
.ds-btn[aria-busy="true"] { pointer-events: none; }
.ds-btn[aria-busy="true"] > .ds-btn__label { visibility: hidden; }

/* ---- Status chip ------------------------------------------------------- */
.ds-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--ds-chip-h);
  padding: 0 10px;
  border-radius: var(--ds-radius-pill);
  border: 1px solid currentColor;
  font: var(--ds-label);
  white-space: nowrap;
}
.ds-chip__icon { width: var(--ds-icon); height: var(--ds-icon); flex: none; }
.ds-chip--open       { color: var(--ds-state-open);       background: var(--ds-state-open-fill); }
.ds-chip--in-kitchen { color: var(--ds-state-in-kitchen); background: var(--ds-state-in-kitchen-fill); }
.ds-chip--served     { color: var(--ds-state-served);     background: var(--ds-state-served-fill); }
.ds-chip--billed     { color: var(--ds-state-billed);     background: var(--ds-state-billed-fill); }
.ds-chip--cancelled  { color: var(--ds-state-cancelled);  background: var(--ds-state-cancelled-fill); }
.ds-chip--pending    { color: var(--ds-state-pending);    background: var(--ds-state-pending-fill); }
.ds-chip--settled    { color: var(--ds-state-settled);    background: var(--ds-state-settled-fill); }
.ds-chip--failed     { color: var(--ds-state-failed);     background: var(--ds-state-failed-fill); }
.ds-chip--refunded   { color: var(--ds-state-refunded);   background: var(--ds-state-refunded-fill); }
.ds-chip--void       { color: var(--ds-state-void);       background: var(--ds-state-void-fill); }
.ds-chip--locked     { color: var(--ds-state-locked);     background: var(--ds-state-locked-fill); }

/* ---- Fields ------------------------------------------------------------ */
.ds-field { display: flex; flex-direction: column; gap: var(--ds-space-1); }
.ds-field__label { font: var(--ds-label); color: var(--ds-text-secondary); }
.ds-field__input {
  min-height: var(--ds-btn-h);
  padding: 0 var(--ds-space-3);
  background: var(--ds-surface);
  color: var(--ds-text);
  border: 1px solid var(--ds-border-strong);
  border-radius: var(--ds-radius-sm);
  font: var(--ds-body);
}
.ds-field__input::placeholder { color: var(--ds-text-tertiary); }
.ds-field__input:focus { border-color: var(--ds-action); }
.ds-field--error .ds-field__input { border-color: var(--ds-mirchi-500); background: var(--ds-mirchi-50); }
.ds-field__message { font: var(--ds-caption); color: var(--ds-mirchi-600); display: flex; align-items: center; gap: 6px; }

/* ---- Ambient bars ------------------------------------------------------ */
.ds-bar {
  display: flex; align-items: center; gap: var(--ds-space-2);
  min-height: 40px;
  padding: 0 var(--ds-pad-x);
  font: var(--ds-label);
}
[data-density="compact"] .ds-bar { min-height: 32px; }
.ds-bar--offline  { background: var(--ds-sync-offline); color: var(--ds-slate-0); }
.ds-bar--queued   { background: var(--ds-haldi-50);  color: var(--ds-haldi-600); }
.ds-bar--failed   { background: var(--ds-mirchi-50); color: var(--ds-mirchi-600); }
.ds-bar--elevated { background: var(--ds-jamun-50);  color: var(--ds-jamun-600); }

/* ---- Data table (compact) ---------------------------------------------- */
.ds-table { width: 100%; border-collapse: collapse; background: var(--ds-surface); }
.ds-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--ds-surface-sunken);
  font: var(--ds-overline); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ds-text-secondary);
  text-align: left; padding: var(--ds-space-2) var(--ds-space-3);
  border-bottom: 1px solid var(--ds-border);
}
.ds-table tbody td { height: var(--ds-row-h); padding: 0 var(--ds-space-3); border-bottom: 1px solid var(--ds-border); }
.ds-table tbody tr:hover { background: var(--ds-action-subtle); }
.ds-table tbody tr[aria-selected="true"] { background: var(--ds-action-subtle); box-shadow: inset 2px 0 0 var(--ds-action); }
.ds-table tfoot td { background: var(--ds-surface-sunken); border-top: 2px solid var(--ds-border-strong); font-weight: 600; }

/* ---- KDS ticket -------------------------------------------------------- */
.ds-ticket {
  background: var(--ds-ticket-normal, var(--ds-surface));
  border: 1px solid var(--ds-border);
  border-left: none;
  border-radius: var(--ds-radius-railed-card);
  box-shadow: none;                 /* forbidden on kitchen by rule */
  padding: var(--ds-pad-y) var(--ds-pad-x);
  padding-left: calc(var(--ds-pad-x) + var(--ds-rail-w));
  position: relative;
}
.ds-ticket--warning { --ds-rail-color: var(--ds-urgency-warning); background: var(--ds-ticket-warning); }
.ds-ticket--overdue { --ds-rail-color: var(--ds-urgency-overdue); background: var(--ds-ticket-overdue); }
.ds-ticket--ready   { --ds-rail-color: var(--ds-urgency-ready);   background: var(--ds-ticket-ready); }
.ds-ticket--void    { background: var(--ds-ticket-void); }
.ds-ticket__timer   { font: var(--ds-numeric); font-variant-numeric: tabular-nums slashed-zero; }
.ds-ticket__changed { background: var(--ds-haldi-300); color: var(--ds-slate-1000); font: var(--ds-label); padding: 4px var(--ds-space-3); margin: 0 calc(var(--ds-pad-x) * -1); }
.ds-ticket__removed { text-decoration: line-through; color: var(--ds-text-tertiary); }

/* New ticket: a 3 s border pulse, then stop. */
@keyframes ds-ticket-pulse {
  0%, 100% { box-shadow: inset 0 0 0 0 var(--ds-action); }
  50%      { box-shadow: inset 0 0 0 3px var(--ds-action); }
}
.ds-ticket--new { animation: ds-ticket-pulse var(--ds-motion-pulse) var(--ds-ease-standard) 1; }

/* ---- Skeleton ---------------------------------------------------------- */
.ds-skeleton {
  background: linear-gradient(90deg, var(--ds-surface-sunken) 25%, var(--ds-border) 50%, var(--ds-surface-sunken) 75%);
  background-size: 200% 100%;
  animation: ds-sweep 1.2s linear infinite;
  border-radius: var(--ds-radius-xs);
}
@keyframes ds-sweep { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ==========================================================================
   Reduced motion. Everything collapses to instant except the KDS pulse, which
   becomes a static border hold in the same colour — the meaning survives.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .ds-ticket--new { box-shadow: inset 0 0 0 3px var(--ds-action); animation: none; }
  .ds-skeleton { animation: none; background: var(--ds-surface-sunken); }
}

/* ==========================================================================
   Thermal print — 58 / 80 mm. Pure black on white; thermal heads render
   mid-tones as noise. Rail patterns become leading glyphs.
   ========================================================================== */

@media print {
  :root { --ds-canvas: #FFF; --ds-surface: #FFF; --ds-text: #000; --ds-border: #000; }
  body { font-family: var(--ds-font-numeric); font-size: 10pt; }
  .ds-railed, .ds-ticket { box-shadow: none; border: none; padding-left: 0; }
  .ds-railed::before, .ds-ticket::before { display: none; }
  .ds-state-open::after,      .ds-rail--dashed::after { content: "\250B "; }
  .ds-rail--double::after     { content: "\2551 "; }
  .ds-rail--hollow::after     { content: "\2591 "; }
  .ds-btn, .ds-bar { display: none; }
}
