﻿/* ==========================================================================
   FONTS — self-hosted, because the office loses internet and §13 says so.

   Noto Sans Arabic covers the Sorani letters ڕ ڵ ۆ ێ ڤ گ چ پ ژ that most
   Arabic fonts omit. It is the UI face for everything.

   Inter carries numerals. The reference specifies Roboto Mono for that role and
   this is a deliberate substitution: Inter is already vetted and licensed in
   this repository, and what the numeral role actually requires is TABULAR
   figures with LTR isolation, which Inter's tnum feature provides. What it must
   never be is a face applied to Kurdish text — a Latin monospace breaks
   Arabic-script letter joining outright — and .cds-num is scoped to numbers
   only, which is the rule that matters.
   ========================================================================== */

@font-face {
  font-family: 'Noto Sans Arabic';
  src: url('fonts/noto-sans-arabic.woff2') format('woff2');
  font-weight: 300 900;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('fonts/inter-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
  /* Latin, digits and punctuation only. Without the range the browser would
     reach for Inter before Noto for Arabic text it cannot actually draw. */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2074, U+20AC, U+2122, U+2212;
}

/* ==========================================================================
   app.css — the design system, and the only place --cds-* tokens are declared.
   DESIGN-DOCTRINE.md is the law; this file is where it is spelled out.

   THREE RULES, in the order they cost us most:

   1. Every theme x mode block declares the ENTIRE Tier-2 contract. No block
      inherits a token from another theme or mode. It is repetitive on purpose:
      a token defined only in a dark block silently falls back to another
      palette's value, which is how a palette ends up with no working light
      mode at all. ThemeTokenContractTests reads this file and fails on a block
      that is short a token.

   2. Never give a colour its only definition inside a media query or a
      [data-theme] block. The full light palette is declared on bare :root
      first; the theme blocks then redefine.

   3. Logical properties only — padding-inline, margin-inline, inset-inline,
      border-inline. Never left/right. AGENTS.md §13 bans the physical
      variants and LogicalPropertyTests enforces it across .razor and .css.
      A mirrored-layout bug is invisible in English and this app has no
      English users.

   Radzen loads before this file (see App.razor). The RADZEN BRIDGE section
   below re-points Radzen's own roots at these tokens, so the library themes
   itself. DECISIONS-006 D-021: that mapping lives here, in CSS, precisely so
   there is no hand-maintained mirror in C# to drift out of step.
   ========================================================================== */

:root {
  --font-kurdish: 'Noto Sans Arabic', 'Segoe UI', Tahoma, Arial, sans-serif;
  --font-mono: 'Inter', 'Noto Sans Arabic', Consolas, monospace;

  /* Structural constants. Flat surfaces: 2px radius, 1px borders, 8px grid. */
  --cds-radius: 2px;
  --cds-border-width: 1px;
  --cds-spacing-01: 0.125rem;
  --cds-spacing-02: 0.25rem;
  --cds-spacing-03: 0.5rem;
  --cds-spacing-04: 0.75rem;
  --cds-spacing-05: 1rem;
  --cds-spacing-06: 1.5rem;
  --cds-spacing-07: 2rem;
  --cds-spacing-08: 3rem;

  /* =====================================================================
     MOTION — duration and easing (DESIGN-DOCTRINE §6, §10).

     Required by §6 and absent until now, which is not a neutral gap: a
     missing token group does not produce an error, it produces a literal.
     `.cds-nav__chevron` carried `transition: transform 0.15s ease` for
     exactly that reason, and it reads as an exception rather than as the
     symptom it is. It consumes these now.

     Values follow Carbon's published scale. That is continuity, not
     borrowing — `--cds-` is Carbon's own prefix and this file already
     speaks its vocabulary (text-primary, layer, support-error, spacing-0N),
     inherited from the React client D-020 deleted. The scale also lands on
     the two numbers POS-UI-CONCEPT.md §10 asks for: 150ms for the item-add
     row flash, 240ms for an overlay slide.

     PRODUCTIVE easing only. Carbon's expressive curves are for moments that
     want to be noticed; a till is the definition of a surface that does
     not. No bounce, no stretch, no sudden stop.
     ===================================================================== */

  --cds-duration-fast-01: 70ms;        /* button, toggle */
  --cds-duration-fast-02: 110ms;       /* fade */
  --cds-duration-moderate-01: 150ms;   /* small expansion; the POS row flash */
  --cds-duration-moderate-02: 240ms;   /* expansion, toast, overlay slide */
  --cds-duration-slow-01: 400ms;       /* large expansion */
  --cds-duration-slow-02: 700ms;       /* background dimming */

  --cds-ease-standard: cubic-bezier(0.2, 0, 0.38, 0.9);
  --cds-ease-entrance: cubic-bezier(0, 0, 0.38, 0.9);
  --cds-ease-exit: cubic-bezier(0.2, 0, 1, 0.9);

  /* =====================================================================
     ELEVATION (DESIGN-DOCTRINE §6).

     Three levels, and deliberately restrained: §10 of the doctrine calls
     for "layered surfaces, subtle elevation + 1px hairline borders", and
     this file's structural constants are already flat — 2px radius, 1px
     borders. Depth here separates surfaces; it never decorates them.

     The dark-mode values are NOT the same shadows at a different opacity.
     A shadow is the absence of light, so on a near-black surface it barely
     reads at all and the border does the separating instead. Dark therefore
     gets a deeper, tighter shadow and leans on --cds-border-subtle.
     ===================================================================== */

  --cds-elevation-01: 0 1px 2px hsl(230 22% 6% / 0.06);
  --cds-elevation-02: 0 2px 8px hsl(230 22% 6% / 0.10);
  --cds-elevation-03: 0 8px 24px hsl(230 22% 6% / 0.14);

  /* =====================================================================
     TYPE SCALE, and the numeric family (DESIGN-DOCTRINE §6, §8).

     §8.2 is the reason --cds-font-numeric exists as a token of its own:
     "Tabular numerals everywhere money appears. Decimal points align down
     the column." A proportional font puts a 1 narrower than a 7 and the
     column stops aligning, which on a ledger is not a cosmetic problem —
     it is how a wrong figure stops looking wrong.

     Apply it through .cds-num rather than by hand: the family alone does
     not switch on tabular figures, font-variant-numeric does.
     ===================================================================== */

  --cds-font-numeric: var(--font-mono);

  /*
     Drawn from the sizes this application already uses, counted, not from a
     reference scale imposed on top of it. The first draft of this block was
     Carbon's ramp and it was wrong here: it had no 13px step, which is the
     single most common size in this file (twenty uses) and is what a grid
     cell and body copy are actually set in. A scale nothing can adopt
     without a visual change is a scale nobody adopts.

     07 is the largest size in the app today. 08 exists for one thing and is
     deliberately far above the rest: POS-UI-CONCEPT.md §4.1, "TOTAL is the
     largest text on screen."
  */
  --cds-type-01: 0.6875rem;   /* 11px — captions, hints */
  --cds-type-02: 0.75rem;     /* 12px — labels */
  --cds-type-03: 0.8125rem;   /* 13px — body, grid cells: the default here */
  --cds-type-04: 0.875rem;    /* 14px — body large */
  --cds-type-05: 1rem;        /* 16px — lead */
  --cds-type-06: 1.25rem;     /* 20px — section heading */
  --cds-type-07: 1.375rem;    /* 22px — page heading */
  --cds-type-08: 3.5rem;      /* 56px — the POS total, and nothing else */

  /* =====================================================================
     DENSITY (DESIGN-DOCTRINE §6, D-004).

     D-004 settles density by the PURPOSE of the surface, not by who is
     looking: compact for grids, ledgers, statements and POS; comfortable
     for dashboard, settings, onboarding and document review.

     The floor is the part that gets broken. D-004: "compact means less
     padding, NEVER a smaller hit area", and §11 puts that floor at 44px.
     So the two are separate tokens on purpose — --cds-density-row-h may
     shrink, --cds-target-min may not, and anything interactive sizes
     against the second. A row is not a hit area; a button inside it is.
     ===================================================================== */

  --cds-target-min: 44px;            /* §11. Never overridden, in any density. */
  --cds-density-row-h: 2.75rem;      /* comfortable */
  --cds-density-pad: var(--cds-spacing-04);

  /* =====================================================================
     Z-LAYERS.

     Five literals were scattered through this file before these existed
     (20, 30, 9000, 10000, 10001) and nothing said how they related. They
     do relate, and the order is load-bearing at the top: the reconnect
     overlay must outrank every overlay, because it appears exactly when
     the circuit that would have closed them is gone.
     ===================================================================== */

  --cds-z-sticky: 20;       /* app bar */
  --cds-z-nav: 30;          /* the rail, when it floats over content */
  --cds-z-overlay: 9000;    /* command palette, POS payment/journal overlays */
  --cds-z-reconnect: 10000; /* the circuit is gone — above everything */
  --cds-z-error: 10001;     /* except the error bar */

  /* =====================================================================
     POS SEMANTIC ALIASES (POS-UI-CONCEPT.md §10).

     Aliases, not new colours, and that is the whole trick: they reference
     the theme tokens through var(), so one declaration here is correct in
     all five palettes and both modes without being restated ten times.
     Custom properties substitute at computed-value time against the same
     element — and both these and the theme blocks land on <html> — so a
     theme that redefines --cds-support-warning redefines stock-low with it.

     Stock state must not rely on colour alone (§11): the tile carries an
     edge bar AND a label. These are the bar.
     ===================================================================== */

  --cds-pos-stock-ok: var(--cds-support-success);
  --cds-pos-stock-low: var(--cds-support-warning);
  --cds-pos-stock-out: var(--cds-support-error);

  /* §4.1: "TOTAL is the largest text on screen." Said once, here. */
  --cds-pos-total-size: var(--cds-type-08);
  --cds-pos-flash: var(--cds-duration-moderate-01);
  --cds-pos-slide: var(--cds-duration-moderate-02);

  /* =====================================================================
     TIER 1 — PRIMITIVES

     Raw ramps, namespaced per palette. Nothing outside this section may
     reference a Tier-1 variable; components consume only the Tier-2 --cds-*
     contract below.

     SATURATION DOCTRINE — this is a financial application, not a showcase.
     Surfaces, borders and body text come from a NEAR-NEUTRAL ramp (8-24%
     saturation) so a ledger stays legible for hours. A palette's identity
     lives in its accent ramp, used only for interactive elements, tile accent
     bars and tags. Tinting the canvas with a 90%-saturated brand hue turns a
     balance sheet into a poster.

     Ramp lightness: 50=97% 100=94% 150=89% 200=84% 300=72% 400=58%
     500=48% 600=40% 700=30% 850=15% 900=11% 925=8% 950=6%
     (850->950 is the dark-UI elevation band: hover -> field -> card -> canvas.)
     ===================================================================== */

  /* 1 · COOL COASTAL — indigo brand on a cool grey room */
  --cc-slate-50:  hsl(220 14% 97%);   --cc-slate-100: hsl(220 14% 94%);
  --cc-slate-150: hsl(220 13% 89%);   --cc-slate-200: hsl(220 13% 84%);
  --cc-slate-300: hsl(220 12% 72%);   --cc-slate-400: hsl(220 10% 58%);
  --cc-slate-500: hsl(220 10% 48%);   --cc-slate-600: hsl(220 11% 40%);
  --cc-slate-700: hsl(222 14% 30%);   --cc-slate-800: hsl(224 16% 20%);
  --cc-slate-850: hsl(225 16% 15%);   --cc-slate-900: hsl(226 18% 11%);
  --cc-slate-925: hsl(228 20% 8%);    --cc-slate-950: hsl(230 22% 6%);

  --cc-indigo-300: hsl(238 24% 72%);  --cc-indigo-400: hsl(238 22% 64%);
  --cc-indigo-600: hsl(238 20% 40%);  --cc-indigo-700: hsl(238 22% 31%);

  --cc-teal-300: hsl(196 24% 72%);    --cc-teal-700: hsl(200 30% 32%);

  /* 2 · OCEAN BREEZE — sapphire and aqua over a cool navy-grey room */
  --ob-slate-50:  hsl(212 24% 97%);   --ob-slate-100: hsl(212 24% 94%);
  --ob-slate-150: hsl(212 22% 89%);   --ob-slate-200: hsl(212 20% 84%);
  --ob-slate-300: hsl(212 18% 72%);   --ob-slate-400: hsl(212 16% 58%);
  --ob-slate-500: hsl(212 16% 48%);   --ob-slate-600: hsl(213 18% 40%);
  --ob-slate-700: hsl(214 22% 30%);   --ob-slate-800: hsl(215 28% 20%);
  --ob-slate-850: hsl(216 30% 15%);   --ob-slate-900: hsl(217 33% 11%);
  --ob-slate-925: hsl(218 36% 8%);    --ob-slate-950: hsl(219 40% 6%);

  --ob-sapphire-300: hsl(226 70% 74%); --ob-sapphire-400: hsl(226 62% 66%);
  --ob-sapphire-600: hsl(224 55% 42%); --ob-sapphire-700: hsl(224 58% 34%);

  --ob-aqua-300: hsl(190 62% 62%);    --ob-aqua-700: hsl(192 70% 28%);

  /* 3 · GOLDEN TWILIGHT — gold on black. The gold is the identity: it stays
     the interactive colour in BOTH modes and carries near-black text, which
     reads at 9.5:1. Swapping it out in light mode would leave a blue theme on
     cream paper — the palette would stop being itself. */
  --gt-stone-50:  hsl(40 12% 97%);    --gt-stone-100: hsl(40 11% 94%);
  --gt-stone-150: hsl(40 10% 89%);    --gt-stone-200: hsl(40 10% 84%);
  --gt-stone-300: hsl(40 9% 72%);     --gt-stone-400: hsl(38 8% 58%);
  --gt-stone-500: hsl(38 9% 48%);     --gt-stone-600: hsl(38 9% 40%);
  --gt-stone-700: hsl(38 11% 30%);    --gt-stone-950: hsl(40 10% 8%);

  --gt-navy-500: hsl(219 26% 52%);    --gt-navy-600: hsl(219 30% 40%);
  --gt-navy-800: hsl(220 38% 20%);
  --gt-navy-850: hsl(220 42% 14%);    --gt-navy-900: hsl(220 45% 10%);
  --gt-navy-925: hsl(221 48% 7%);

  --gt-black: hsl(0 0% 0%);           /* the black in Black & Gold */

  --gt-gold-300: hsl(38 95% 68%);     --gt-gold-400: hsl(38 95% 60%);
  --gt-gold-500: hsl(38 95% 53%);     --gt-gold-600: hsl(38 95% 45%);
  --gt-gold-750: hsl(40 90% 27%);

  /* 4 · SUNNY BEACH — paprika and tangerine over a warm neutral room */
  --sb-sand-50:  hsl(30 20% 97%);     --sb-sand-100: hsl(30 18% 94%);
  --sb-sand-150: hsl(30 16% 89%);     --sb-sand-200: hsl(30 15% 84%);
  --sb-sand-300: hsl(30 13% 72%);     --sb-sand-400: hsl(28 10% 58%);
  --sb-sand-500: hsl(28 10% 48%);     --sb-sand-600: hsl(26 12% 40%);
  --sb-sand-700: hsl(24 14% 30%);     --sb-sand-800: hsl(24 16% 20%);
  --sb-sand-850: hsl(22 18% 14%);     --sb-sand-900: hsl(22 20% 10%);
  --sb-sand-925: hsl(22 22% 8%);      --sb-sand-950: hsl(22 24% 6%);

  --sb-paprika-300: hsl(16 88% 70%);  --sb-paprika-400: hsl(16 85% 64%);
  --sb-paprika-600: hsl(14 80% 42%);  --sb-paprika-700: hsl(14 82% 34%);

  --sb-teal-300: hsl(185 45% 62%);    --sb-teal-700: hsl(186 50% 28%);

  /* 5 · DEEP SEA — warm sand on cold desaturated navy. The quietest palette. */
  --ds-navy-50:  hsl(214 22% 97%);    --ds-navy-100: hsl(214 22% 94%);
  --ds-navy-150: hsl(214 20% 89%);    --ds-navy-200: hsl(214 18% 84%);
  --ds-navy-300: hsl(214 16% 72%);    --ds-navy-400: hsl(214 14% 58%);
  --ds-navy-500: hsl(214 14% 48%);    --ds-navy-600: hsl(214 16% 40%);
  --ds-navy-700: hsl(215 20% 30%);    --ds-navy-800: hsl(216 24% 20%);
  --ds-navy-850: hsl(216 26% 14%);    --ds-navy-900: hsl(217 28% 10%);
  --ds-navy-925: hsl(217 30% 8%);     --ds-navy-950: hsl(218 32% 6%);

  --ds-steel-300: hsl(210 45% 70%);   --ds-steel-400: hsl(210 42% 60%);
  --ds-steel-600: hsl(210 40% 38%);   --ds-steel-700: hsl(211 44% 30%);

  --ds-sand-300: hsl(42 48% 72%);     --ds-sand-400: hsl(42 45% 64%);
  --ds-sand-700: hsl(42 45% 29%);

  /* SHARED — functional status ramps.
     Deliberately NOT drawn from any brand palette: in an accounting system
     green must always mean gain and red loss, so the meaning of a figure can
     never shift because somebody changed theme. DESIGN-DOCTRINE §8. */
  --status-success-300: hsl(152 55% 68%);  --status-success-600: hsl(152 58% 33%);
  --status-warning-300: hsl(38 92% 70%);   --status-warning-700: hsl(38 92% 30%);
  --status-danger-300:  hsl(0 84% 72%);    --status-danger-600:  hsl(0 74% 40%);
  --status-info-300:    hsl(206 88% 72%);  --status-info-600:    hsl(206 84% 36%);

  /* =====================================================================
     TIER 2 — THE SEMANTIC CONTRACT

     These twenty-five COLOUR tokens are the part of the vocabulary that
     changes with theme and mode, and the only part every theme block must
     restate. The structural groups above — motion, elevation, type,
     density, z-layers and the POS aliases — are declared once and are
     theme-invariant, because a duration or a hit-target floor that varied
     by palette would be a bug, not a feature.
     The values here are the fallback set — Cool Coastal, light — in force
     before a data-theme attribute is present, i.e. during prerender and for
     a browser with localStorage blocked. Rule 2 at the top of this file is
     why they are declared on bare :root and not only inside a theme block.
     ===================================================================== */

  --cds-background: hsl(220 14% 94%);
  --cds-layer: #ffffff;
  --cds-layer-accent: hsl(220 14% 97%);
  --cds-layer-hover: hsl(220 13% 89%);
  --cds-border-subtle: hsl(220 13% 89%);
  --cds-border-strong: hsl(220 10% 58%);
  --cds-text-primary: hsl(230 22% 6%);
  --cds-text-secondary: hsl(222 14% 30%);
  --cds-text-helper: hsl(220 11% 40%);
  --cds-text-on-color: #ffffff;
  --cds-interactive: hsl(238 20% 40%);
  --cds-interactive-hover: hsl(238 22% 31%);
  --cds-link: var(--cds-interactive);
  --cds-accent: hsl(200 30% 32%);
  --cds-focus: hsl(238 20% 40%);
  --cds-support-error: var(--status-danger-600);
  --cds-support-success: var(--status-success-600);
  --cds-support-warning: var(--status-warning-700);
  --cds-support-info: var(--status-info-600);
  --cds-usd: var(--status-success-600);
  --cds-iqd: var(--status-warning-700);
  --cds-shell-bg: hsl(230 22% 6%);
  --cds-shell-layer: hsl(226 18% 11%);
  --cds-shell-border: hsl(225 16% 15%);
  --cds-shell-text: hsl(220 12% 72%);
  --cds-shell-text-active: #ffffff;
}

/* ==========================================================================
   MODE LAYER — native colour-scheme only.

   Deliberately theme-invariant. Only the light/dark rendering of the browser's
   own controls changes here; the functional colours' MEANING never does. An
   accountant must never see profit-green or loss-red shift because a theme
   changed, which is why the status ramps are shared and every theme block
   restates them rather than overriding them.
   ========================================================================== */

:root:not(.dark) { color-scheme: light; }
:root.dark       { color-scheme: dark; }

/*
  Elevation is the one non-colour group that still has to change with mode.
  A shadow is the absence of light: cast onto a near-black surface the same
  shadow simply disappears, and the card stops reading as a separate plane.
  Dark goes deeper and tighter, and lets the hairline border carry more of
  the separation. Theme-invariant, like the status ramps above it — depth
  means the same thing in every palette.
*/
:root.dark {
  --cds-elevation-01: 0 1px 2px hsl(0 0% 0% / 0.40);
  --cds-elevation-02: 0 2px 8px hsl(0 0% 0% / 0.50);
  --cds-elevation-03: 0 8px 24px hsl(0 0% 0% / 0.60);
}

/*
  Compact density (D-004). Applied to a SURFACE, not to the user: the shell
  stamps it on grids, ledgers, statements and the till, and leaves the
  dashboard and settings comfortable.

  --cds-target-min is deliberately absent here. That is the point of it
  being a separate token: compact takes padding out of a row, and never
  takes a hit area out of the control inside it (§11, 44px floor).
*/
[data-density="compact"] {
  --cds-density-row-h: 2rem;
  --cds-density-pad: var(--cds-spacing-02);
}

/* ==========================================================================
   THEME x MODE BLOCKS

   data-theme and .dark are stamped on <html> — by the pre-paint script in
   App.razor before first render, and by ThemeService at runtime. On <html>
   rather than a layout wrapper for one specific reason: Radzen renders
   dialogs, popups and tooltips into a body-level container OUTSIDE the
   layout, and its RTL rules are descendant selectors on *[dir=rtl]. A theme
   stamped on a wrapper leaves every overlay unthemed and left-to-right.
   DECISIONS-006 D-021.

   Because the attribute is on the root element, no :has() indirection is
   needed: these selectors match <html> itself and the custom properties
   inherit to everything, overlays included.
   ========================================================================== */

  /* --- COOL COASTAL - indigo on cool grey -------------------------------- */
  [data-theme="cool-coastal"]:not(.dark) {
    --cds-background: var(--cc-slate-100);
    --cds-layer: #ffffff;
    --cds-layer-accent: var(--cc-slate-50);
    --cds-layer-hover: var(--cc-slate-150);
    --cds-border-subtle: var(--cc-slate-150);
    --cds-border-strong: var(--cc-slate-400);
    --cds-text-primary: var(--cc-slate-950);
    --cds-text-secondary: var(--cc-slate-700);
    --cds-text-helper: var(--cc-slate-600);
    --cds-text-on-color: #ffffff;
    --cds-interactive: var(--cc-indigo-600);
    --cds-interactive-hover: var(--cc-indigo-700);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--cc-teal-700);
    --cds-focus: var(--cc-indigo-600);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-600);
    --cds-support-success: var(--status-success-600);
    --cds-support-warning: var(--status-warning-700);
    --cds-support-info: var(--status-info-600);
    --cds-usd: var(--status-success-600);
    --cds-iqd: var(--status-warning-700);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--cc-slate-950);
    --cds-shell-layer: var(--cc-slate-900);
    --cds-shell-border: var(--cc-slate-850);
    --cds-shell-text: var(--cc-slate-300);
    --cds-shell-text-active: #ffffff;
  }

  [data-theme="cool-coastal"].dark {
    --cds-background: var(--cc-slate-950);
    --cds-layer: var(--cc-slate-925);
    --cds-layer-accent: var(--cc-slate-900);
    --cds-layer-hover: var(--cc-slate-850);
    --cds-border-subtle: var(--cc-slate-800);
    --cds-border-strong: var(--cc-slate-500);
    --cds-text-primary: var(--cc-slate-50);
    --cds-text-secondary: var(--cc-slate-200);
    --cds-text-helper: var(--cc-slate-400);
    --cds-text-on-color: var(--cc-slate-950);
    --cds-interactive: var(--cc-indigo-400);
    --cds-interactive-hover: var(--cc-indigo-300);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--cc-teal-300);
    --cds-focus: var(--cc-indigo-300);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-300);
    --cds-support-success: var(--status-success-300);
    --cds-support-warning: var(--status-warning-300);
    --cds-support-info: var(--status-info-300);
    --cds-usd: var(--status-success-300);
    --cds-iqd: var(--status-warning-300);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--cc-slate-950);
    --cds-shell-layer: var(--cc-slate-900);
    --cds-shell-border: var(--cc-slate-850);
    --cds-shell-text: var(--cc-slate-300);
    --cds-shell-text-active: #ffffff;
  }

  /* --- OCEAN BREEZE - sapphire / aqua ------------------------------------ */
  [data-theme="ocean-breeze"]:not(.dark) {
    --cds-background: var(--ob-slate-100);
    --cds-layer: #ffffff;
    --cds-layer-accent: var(--ob-slate-50);
    --cds-layer-hover: var(--ob-slate-150);
    --cds-border-subtle: var(--ob-slate-150);
    --cds-border-strong: var(--ob-slate-400);
    --cds-text-primary: var(--ob-slate-950);
    --cds-text-secondary: var(--ob-slate-700);
    --cds-text-helper: var(--ob-slate-600);
    --cds-text-on-color: #ffffff;
    --cds-interactive: var(--ob-sapphire-600);
    --cds-interactive-hover: var(--ob-sapphire-700);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--ob-aqua-700);
    --cds-focus: var(--ob-sapphire-600);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-600);
    --cds-support-success: var(--status-success-600);
    --cds-support-warning: var(--status-warning-700);
    --cds-support-info: var(--status-info-600);
    --cds-usd: var(--status-success-600);
    --cds-iqd: var(--status-warning-700);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--ob-slate-950);
    --cds-shell-layer: var(--ob-slate-900);
    --cds-shell-border: var(--ob-slate-850);
    --cds-shell-text: var(--ob-slate-300);
    --cds-shell-text-active: #ffffff;
  }

  [data-theme="ocean-breeze"].dark {
    --cds-background: var(--ob-slate-950);
    --cds-layer: var(--ob-slate-925);
    --cds-layer-accent: var(--ob-slate-900);
    --cds-layer-hover: var(--ob-slate-850);
    --cds-border-subtle: var(--ob-slate-800);
    --cds-border-strong: var(--ob-slate-500);
    --cds-text-primary: var(--ob-slate-50);
    --cds-text-secondary: var(--ob-slate-200);
    --cds-text-helper: var(--ob-slate-400);
    --cds-text-on-color: var(--ob-slate-950);
    --cds-interactive: var(--ob-sapphire-400);
    --cds-interactive-hover: var(--ob-sapphire-300);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--ob-aqua-300);
    --cds-focus: var(--ob-sapphire-300);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-300);
    --cds-support-success: var(--status-success-300);
    --cds-support-warning: var(--status-warning-300);
    --cds-support-info: var(--status-info-300);
    --cds-usd: var(--status-success-300);
    --cds-iqd: var(--status-warning-300);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--ob-slate-950);
    --cds-shell-layer: var(--ob-slate-900);
    --cds-shell-border: var(--ob-slate-850);
    --cds-shell-text: var(--ob-slate-300);
    --cds-shell-text-active: #ffffff;
  }

  /* --- GOLDEN TWILIGHT - gold on black ----------------------------------- */
  [data-theme="golden-twilight"]:not(.dark) {
    /* gold stays interactive in both modes and carries near-black text */
    --cds-background: var(--gt-stone-100);
    --cds-layer: #ffffff;
    --cds-layer-accent: var(--gt-stone-50);
    --cds-layer-hover: var(--gt-stone-150);
    --cds-border-subtle: var(--gt-stone-150);
    --cds-border-strong: var(--gt-stone-500);
    --cds-text-primary: var(--gt-stone-950);
    --cds-text-secondary: var(--gt-stone-700);
    --cds-text-helper: var(--gt-stone-600);
    --cds-text-on-color: var(--gt-stone-950);
    --cds-interactive: var(--gt-gold-500);
    --cds-interactive-hover: var(--gt-gold-600);
    --cds-link: hsl(38 95% 32%);  /* NOT --cds-interactive. That amber is a fill: dark text sits on it.
       As text on white it measures 2.0, and darkening the fill to fix that
       drops its own text to 3.81. Two jobs, opposite requirements. */
    --cds-accent: var(--gt-gold-750);
    --cds-focus: var(--gt-gold-750);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-600);
    --cds-support-success: var(--status-success-600);
    --cds-support-warning: var(--status-warning-700);
    --cds-support-info: var(--status-info-600);
    --cds-usd: var(--status-success-600);
    --cds-iqd: var(--status-warning-700);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--gt-black);
    --cds-shell-layer: var(--gt-navy-900);
    --cds-shell-border: var(--gt-navy-850);
    --cds-shell-text: var(--gt-stone-300);
    --cds-shell-text-active: var(--gt-gold-300);
  }

  [data-theme="golden-twilight"].dark {
    --cds-background: var(--gt-black);
    --cds-layer: var(--gt-navy-925);
    --cds-layer-accent: var(--gt-navy-900);
    --cds-layer-hover: var(--gt-navy-850);
    --cds-border-subtle: var(--gt-navy-800);
    --cds-border-strong: var(--gt-navy-500);
    --cds-text-primary: var(--gt-stone-50);
    --cds-text-secondary: var(--gt-stone-200);
    --cds-text-helper: var(--gt-stone-400);
    --cds-text-on-color: var(--gt-stone-950);
    --cds-interactive: var(--gt-gold-500);
    --cds-interactive-hover: var(--gt-gold-300);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--gt-gold-300);
    --cds-focus: var(--gt-gold-300);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-300);
    --cds-support-success: var(--status-success-300);
    --cds-support-warning: var(--status-warning-300);
    --cds-support-info: var(--status-info-300);
    --cds-usd: var(--status-success-300);
    --cds-iqd: var(--status-warning-300);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--gt-black);
    --cds-shell-layer: var(--gt-navy-900);
    --cds-shell-border: var(--gt-navy-850);
    --cds-shell-text: var(--gt-stone-300);
    --cds-shell-text-active: var(--gt-gold-300);
  }

  /* --- SUNNY BEACH - paprika / tangerine on warm neutral ----------------- */
  [data-theme="sunny-beach"]:not(.dark) {
    --cds-background: var(--sb-sand-100);
    --cds-layer: #ffffff;
    --cds-layer-accent: var(--sb-sand-50);
    --cds-layer-hover: var(--sb-sand-150);
    --cds-border-subtle: var(--sb-sand-150);
    --cds-border-strong: var(--sb-sand-500);
    --cds-text-primary: var(--sb-sand-950);
    --cds-text-secondary: var(--sb-sand-700);
    --cds-text-helper: var(--sb-sand-600);
    --cds-text-on-color: #ffffff;
    --cds-interactive: var(--sb-paprika-600);
    --cds-interactive-hover: var(--sb-paprika-700);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--sb-teal-700);
    --cds-focus: var(--sb-paprika-600);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-600);
    --cds-support-success: var(--status-success-600);
    --cds-support-warning: var(--status-warning-700);
    --cds-support-info: var(--status-info-600);
    --cds-usd: var(--status-success-600);
    --cds-iqd: var(--status-warning-700);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--sb-sand-950);
    --cds-shell-layer: var(--sb-sand-900);
    --cds-shell-border: var(--sb-sand-850);
    --cds-shell-text: var(--sb-sand-300);
    --cds-shell-text-active: #ffffff;
  }

  [data-theme="sunny-beach"].dark {
    --cds-background: var(--sb-sand-950);
    --cds-layer: var(--sb-sand-925);
    --cds-layer-accent: var(--sb-sand-900);
    --cds-layer-hover: var(--sb-sand-850);
    --cds-border-subtle: var(--sb-sand-800);
    --cds-border-strong: var(--sb-sand-500);
    --cds-text-primary: var(--sb-sand-50);
    --cds-text-secondary: var(--sb-sand-200);
    --cds-text-helper: var(--sb-sand-400);
    --cds-text-on-color: var(--sb-sand-950);
    --cds-interactive: var(--sb-paprika-400);
    --cds-interactive-hover: var(--sb-paprika-300);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--sb-teal-300);
    --cds-focus: var(--sb-paprika-300);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-300);
    --cds-support-success: var(--status-success-300);
    --cds-support-warning: var(--status-warning-300);
    --cds-support-info: var(--status-info-300);
    --cds-usd: var(--status-success-300);
    --cds-iqd: var(--status-warning-300);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--sb-sand-950);
    --cds-shell-layer: var(--sb-sand-900);
    --cds-shell-border: var(--sb-sand-850);
    --cds-shell-text: var(--sb-sand-300);
    --cds-shell-text-active: #ffffff;
  }

  /* --- DEEP SEA - warm sand on cold navy --------------------------------- */
  [data-theme="deep-sea"]:not(.dark) {
    /* sand leads in dark, steel supports - the reverse of light */
    --cds-background: var(--ds-navy-100);
    --cds-layer: #ffffff;
    --cds-layer-accent: var(--ds-navy-50);
    --cds-layer-hover: var(--ds-navy-150);
    --cds-border-subtle: var(--ds-navy-150);
    --cds-border-strong: var(--ds-navy-400);
    --cds-text-primary: var(--ds-navy-950);
    --cds-text-secondary: var(--ds-navy-700);
    --cds-text-helper: var(--ds-navy-600);
    --cds-text-on-color: #ffffff;
    --cds-interactive: var(--ds-steel-600);
    --cds-interactive-hover: var(--ds-steel-700);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--ds-sand-700);
    --cds-focus: var(--ds-steel-600);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-600);
    --cds-support-success: var(--status-success-600);
    --cds-support-warning: var(--status-warning-700);
    --cds-support-info: var(--status-info-600);
    --cds-usd: var(--status-success-600);
    --cds-iqd: var(--status-warning-700);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--ds-navy-950);
    --cds-shell-layer: var(--ds-navy-900);
    --cds-shell-border: var(--ds-navy-850);
    --cds-shell-text: var(--ds-navy-300);
    --cds-shell-text-active: #ffffff;
  }

  [data-theme="deep-sea"].dark {
    --cds-background: var(--ds-navy-950);
    --cds-layer: var(--ds-navy-925);
    --cds-layer-accent: var(--ds-navy-900);
    --cds-layer-hover: var(--ds-navy-850);
    --cds-border-subtle: var(--ds-navy-800);
    --cds-border-strong: var(--ds-navy-500);
    --cds-text-primary: var(--ds-navy-50);
    --cds-text-secondary: var(--ds-navy-200);
    --cds-text-helper: var(--ds-navy-400);
    --cds-text-on-color: var(--ds-navy-950);
    --cds-interactive: var(--ds-sand-400);
    --cds-interactive-hover: var(--ds-sand-300);
    --cds-link: var(--cds-interactive);
    --cds-accent: var(--ds-steel-300);
    --cds-focus: var(--ds-sand-300);
    /* Declared in every block on purpose: no block inherits. */
    --cds-support-error: var(--status-danger-300);
    --cds-support-success: var(--status-success-300);
    --cds-support-warning: var(--status-warning-300);
    --cds-support-info: var(--status-info-300);
    --cds-usd: var(--status-success-300);
    --cds-iqd: var(--status-warning-300);
    /* The navigation shell is dark in every theme and both modes. */
    --cds-shell-bg: var(--ds-navy-950);
    --cds-shell-layer: var(--ds-navy-900);
    --cds-shell-border: var(--ds-navy-850);
    --cds-shell-text: var(--ds-navy-300);
    --cds-shell-text-active: #ffffff;
  }

/* ==========================================================================
   RADZEN BRIDGE — the library themes itself from the contract above.

   Radzen ships 1,761 custom properties on :root, but roughly half of them
   derive from a handful of roots via var(): --rz-panel-background-color,
   --rz-dialog-background-color, --rz-input-background-color and
   --rz-card-background-color all resolve to --rz-base-background-color, and
   --rz-grid-cell-color resolves to --rz-text-color, which resolves to
   --rz-base-800. Re-point that handful and the whole library follows.

   This is the mapping DECISIONS-006 D-021 chose over porting the reference's
   AppTheme.cs. That file was a hand-maintained C# mirror of these same tokens
   and the reference is candid that it drifts ("re-derive it whenever a colour
   changes in app.css"). An assignment in the same file as the definition
   cannot drift.

   The ramp mapping is deliberately not monotonic. Radzen reads its base ramp
   as 50-lightest to 900-darkest; the --cds-* tokens already invert with the
   mode, so pointing base-800 at --cds-text-primary yields dark text on light
   and light text on dark without a second dark-mode block here.
   ========================================================================== */

:root {
  /* Surfaces. Everything panel-, card-, dialog- and input-shaped follows these. */
  --rz-base-background-color: var(--cds-layer);
  --rz-body-background-color: var(--cds-background);

  /* The base ramp, read through the elevation semantics of the contract. */
  --rz-base:     var(--cds-layer-hover);
  --rz-base-50:  var(--cds-layer-accent);
  --rz-base-100: var(--cds-background);
  --rz-base-200: var(--cds-layer-hover);
  --rz-base-300: var(--cds-border-subtle);
  --rz-base-400: var(--cds-border-strong);
  --rz-base-500: var(--cds-text-helper);
  --rz-base-600: var(--cds-text-helper);
  --rz-base-700: var(--cds-text-secondary);
  --rz-base-800: var(--cds-text-primary);
  --rz-base-900: var(--cds-text-primary);
  --rz-base-light:   var(--cds-layer-accent);
  --rz-base-lighter: var(--cds-layer);
  --rz-base-dark:    var(--cds-border-strong);
  --rz-base-darker:  var(--cds-text-primary);

  /*
     THE TEXT ON A BASE BUTTON, and the reason every ButtonStyle.Base in the
     application was unreadable in dark mode.

     The bridge below re-points --rz-on-primary and --rz-on-secondary and
     stopped there. --rz-on-base kept Radzen's own #212121, so a Base button
     drew near-black text on the near-black surface --rz-base had correctly
     been re-pointed at: measured at a contrast ratio of 1.03, against the
     4.5 that text needs. 151 buttons across 47 screens.

     Fixed here rather than in the five dark palette blocks because the
     omission is in the bridge, not in any palette: --cds-text-primary
     already resolves per palette and per mode, so one line covers all ten
     combinations.
  */
  --rz-on-base: var(--cds-text-primary);

  /* Text. Set explicitly rather than left to derive, so a Radzen release that
     re-points --rz-text-color at something else cannot silently unthemed it. */
  --rz-text-color: var(--cds-text-primary);
  --rz-text-title-color: var(--cds-text-primary);
  --rz-text-secondary-color: var(--cds-text-secondary);
  --rz-text-tertiary-color: var(--cds-text-helper);
  --rz-text-disabled-color: var(--cds-text-helper);
  --rz-text-contrast-color: var(--cds-text-on-color);
  --rz-text-font-family: var(--font-kurdish);

  /* Brand. Radzen's "primary" is the contract's interactive colour. */
  --rz-primary: var(--cds-interactive);
  --rz-primary-light: var(--cds-interactive);
  --rz-primary-lighter: var(--cds-layer-accent);
  --rz-primary-dark: var(--cds-interactive-hover);
  --rz-primary-darker: var(--cds-interactive-hover);
  --rz-on-primary: var(--cds-text-on-color);
  --rz-on-primary-light: var(--cds-text-on-color);
  --rz-on-primary-dark: var(--cds-text-on-color);

  --rz-secondary: var(--cds-accent);
  --rz-secondary-dark: var(--cds-accent);
  --rz-on-secondary: var(--cds-text-on-color);

  /* Status. These carry MEANING (§8): they are the shared ramps, never a
     palette's own colours, so green is gain in every theme. */
  --rz-danger:  var(--cds-support-error);
  --rz-success: var(--cds-support-success);
  --rz-warning: var(--cds-support-warning);
  --rz-info:    var(--cds-support-info);
  --rz-danger-dark:  var(--cds-support-error);
  --rz-success-dark: var(--cds-support-success);
  --rz-warning-dark: var(--cds-support-warning);
  --rz-info-dark:    var(--cds-support-info);
  --rz-on-danger:  var(--cds-text-on-color);
  --rz-on-success: var(--cds-text-on-color);
  --rz-on-warning: var(--cds-text-on-color);
  --rz-on-info:    var(--cds-text-on-color);

  /* Flat surfaces: 2px radius, 1px borders, no elevation theatre. */
  --rz-border-radius: var(--cds-radius);
  --rz-border-width: var(--cds-border-width);
  --rz-shadow-0: none; --rz-shadow-1: none; --rz-shadow-2: none;
  --rz-shadow-3: none; --rz-shadow-4: none; --rz-shadow-5: none;

  /* Overlays are the one place a shadow earns its keep — it is what separates
     a dialog from the page behind it when both are the same layer colour. */
  --rz-shadow-6:  0 4px 16px rgb(0 0 0 / 0.24);
  --rz-shadow-7:  0 4px 16px rgb(0 0 0 / 0.24);
  --rz-shadow-8:  0 8px 28px rgb(0 0 0 / 0.28);
  --rz-shadow-9:  0 8px 28px rgb(0 0 0 / 0.28);
  --rz-shadow-10: 0 8px 28px rgb(0 0 0 / 0.28);

  --rz-grid-hover-background-color: var(--cds-layer-hover);
  --rz-grid-stripe-background-color: var(--cds-layer-accent);
  --rz-grid-cell-border: var(--cds-border-width) solid var(--cds-border-subtle);
  --rz-input-focus-shadow: none;
  --rz-input-focus-border: var(--cds-border-width) solid var(--cds-focus);
}

/* ==========================================================================
   BASE
   ========================================================================== */

*, *::before, *::after {
  box-sizing: border-box;
  /* Arabic and Kurdish are cursive scripts: any letter-spacing breaks the
     joins between letters and the word stops being readable. Radzen sets
     letter-spacing on several type ramps, hence the override. */
  letter-spacing: normal !important;
}

html {
  font-family: var(--font-kurdish);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--cds-background);
  color: var(--cds-text-primary);
  font-size: var(--cds-type-04);
  line-height: 1.5;
}

/* Direction follows the language, not the theme (§13). Kurdish and Arabic are
   right-to-left, English is not; App.razor stamps dir on <html> from the
   request culture and the pre-paint script keeps it in step. Everything below
   uses logical properties, so nothing else in this file needs to know. */
html[dir="rtl"] { text-align: right; }
html[dir="ltr"] { text-align: left; }

/* Visible keyboard focus, themed per palette. WCAG 2.1 AA, DESIGN-DOCTRINE §11:
   never outline:none without a designed replacement. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--cds-focus);
  outline-offset: 2px;
}

/* Every number, code and identifier: monospace, tabular, and isolated LTR.
   Without the isolation a currency amount or a document number reorders inside
   RTL text — 1,500 renders as 500,1 and a minus sign lands on the wrong end.
   inline-block is what stops an isolated LTR number collapsing to a zero-height
   inline box inside RTL text: it is a typographic fix, not a layout one. */
.cds-num {
  font-family: var(--cds-font-numeric);
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
  display: inline-block;
}

/* ...but never where display carries layout meaning. A <td class="cds-num">
   turned into an inline-block stops being a table cell: the row loses that
   column and every later cell wraps under the wrong heading. The isolation
   stays; only the display override is withdrawn. */
td.cds-num, th.cds-num { display: table-cell; }

/* ==========================================================================
   LAYOUT — the app shell
   ========================================================================== */

.cds-shell { display: flex; min-height: 100vh; background: var(--cds-background); }

.cds-shell__main {
  flex: 1 1 auto;
  min-width: 0;             /* lets a wide table's own scroller take the overflow */
  display: flex;
  flex-direction: column;
}

.cds-shell__content {
  flex: 1 1 auto;
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--cds-spacing-05);
  padding-block: var(--cds-spacing-06) var(--cds-spacing-08);
}

/*
  The till takes the whole screen; every other page keeps its measure.

  1280px is a reading width and it is right for a ledger or a form — a line of
  figures 1900px wide is hard to track back to its label. A till is not read,
  it is operated: the cap was quietly handing the 240px freed by collapsing the
  rail straight back to the margins, so the work surface never grew at all.
*/
.cds-shell__content--wide {
  max-inline-size: none;
  padding-block: var(--cds-spacing-04);
}

/* The header. Flat, 1px rule underneath, no shadow. */
.cds-appbar {
  position: sticky;
  top: 0;
  z-index: var(--cds-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-03);
  height: 3rem;
  padding-inline: var(--cds-spacing-04);
  background: var(--cds-layer);
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-appbar__spacer { flex: 1 1 auto; }

.cds-appbar__item {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-02);
  height: 100%;
  padding-inline: var(--cds-spacing-04);
  border-inline-end: var(--cds-border-width) solid var(--cds-border-subtle);
  font-size: var(--cds-type-02);
  color: var(--cds-text-secondary);
}

.cds-appbar__item:last-child { border-inline-end: none; padding-inline-end: 0; }

/* Responsive behaviour for cds-* elements lives here as a real media query,
   never as a utility in markup. A utility on a cds-* element is a cascade
   argument waiting to happen, and the reference lost a long afternoon to
   exactly that. */
@media (max-width: 767px) {
  .cds-appbar__item--rate { display: none; }
  .cds-shell__content { padding-inline: var(--cds-spacing-04); }
}

/* --- the navigation rail. Dark in every theme and both modes. --- */
.cds-nav {
  flex: 0 0 15rem;
  width: 15rem;
  background: var(--cds-shell-bg);
  border-inline-end: var(--cds-border-width) solid var(--cds-shell-border);
  display: flex;
  flex-direction: column;
}

.cds-nav__brand {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-03);
  height: 3rem;
  padding-inline: var(--cds-spacing-04);
  border-block-end: var(--cds-border-width) solid var(--cds-shell-border);
  color: var(--cds-shell-text-active);
  font-weight: 600;
}

.cds-nav__list { list-style: none; margin: 0; padding: var(--cds-spacing-03) 0; }

.cds-nav__link {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-03);
  padding-block: var(--cds-spacing-03);
  padding-inline: var(--cds-spacing-05);
  color: var(--cds-shell-text);
  text-decoration: none;
  font-size: var(--cds-type-03);
  border-inline-start: 2px solid transparent;
}

.cds-nav__link:hover { background: var(--cds-shell-layer); color: var(--cds-shell-text-active); }

.cds-nav__link.active {
  background: var(--cds-shell-layer);
  color: var(--cds-shell-text-active);
  border-inline-start-color: var(--cds-interactive);
  font-weight: 600;
}

@media (max-width: 767px) {
  .cds-nav { position: fixed; inset-block: 0; inset-inline-start: 0; z-index: var(--cds-z-nav); transform: translateX(0); }
  html[dir="rtl"] .cds-nav--collapsed { transform: translateX(100%); }
  html[dir="ltr"] .cds-nav--collapsed { transform: translateX(-100%); }
}

/*
  Collapsing on a WIDE screen, which the rail could not do until now.

  The collapsed class existed only as a mobile drawer: below 767px it slid the
  rail off-screen, and above it the rail was 15rem come what may. So the till
  set the state, the toggle reported it, and nothing moved — the work surface
  stayed 820px and the product grid stayed at six columns when it could have
  had eight.

  POS-UI-CONCEPT.md §1 wants zero navigation during a sale, and 15rem of links
  to the back office is a quarter of the width off the thing being used. The
  rail is collapsed, never removed: the app bar toggle brings it straight back,
  which is what keeps a cashier from being stranded on the till.
*/
@media (min-width: 768px) {
  .cds-nav {
    overflow: hidden;
    transition:
      flex-basis var(--cds-duration-moderate-02) var(--cds-ease-standard),
      inline-size var(--cds-duration-moderate-02) var(--cds-ease-standard);
  }

  .cds-nav--collapsed {
    flex-basis: 0;
    inline-size: 0;
    border-inline-end-width: 0;
  }
}

/* ==========================================================================
   SURFACES
   ========================================================================== */

.cds-tile {
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
}

.cds-tile__body { padding: var(--cds-spacing-05); }

/* Wide content scrolls inside its own container, never the page. Verify with
   document.documentElement.scrollWidth > clientWidth staying false. */
.cds-scroll-x { overflow-x: auto; max-width: 100%; }

/* ==========================================================================
   PAGE HEADER
   ========================================================================== */

.cds-page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--cds-spacing-05);
  flex-wrap: wrap;
  margin-block-end: var(--cds-spacing-06);
}

.cds-page-header__overline {
  font-size: var(--cds-type-01);
  font-weight: 600;
  letter-spacing: normal;
  text-transform: uppercase;
  color: var(--cds-text-helper);
}

.cds-page-header__title { margin: 0; font-size: var(--cds-type-07); font-weight: 600; color: var(--cds-text-primary); }
.cds-page-header__subtitle { margin: 0; margin-block-start: var(--cds-spacing-02); font-size: var(--cds-type-03); color: var(--cds-text-secondary); }
.cds-page-header__actions { display: flex; align-items: center; gap: var(--cds-spacing-03); }

/* ==========================================================================
   KPI BAR
   ========================================================================== */

.cds-kpi { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--cds-spacing-04); margin-block-end: var(--cds-spacing-06); }

.cds-kpi__item {
  position: relative;
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  padding: var(--cds-spacing-04) var(--cds-spacing-05);
  /* The accent bar is where a palette's identity is allowed to show. */
  border-inline-start: 3px solid var(--cds-accent);
}

.cds-kpi__label { font-size: var(--cds-type-01); font-weight: 600; text-transform: uppercase; color: var(--cds-text-helper); }
.cds-kpi__value { margin-block-start: var(--cds-spacing-02); font-size: var(--cds-type-06); font-weight: 600; color: var(--cds-text-primary); }
.cds-kpi__hint  { margin-block-start: var(--cds-spacing-01); font-size: var(--cds-type-01); color: var(--cds-text-helper); }

/* ==========================================================================
   PAGE STATES — loading, empty, error.

   Every data page renders all four of loading / error / empty / content.
   A page that handles only the happy path looks broken on a slow query, and
   looks identical whether the query returned nothing or failed.
   ========================================================================== */

.cds-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cds-spacing-04);
  padding: var(--cds-spacing-08) var(--cds-spacing-05);
  text-align: center;
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
}

.cds-state__title { margin: 0; font-size: var(--cds-type-05); font-weight: 600; color: var(--cds-text-primary); }
.cds-state__hint  { margin: 0; font-size: var(--cds-type-03); color: var(--cds-text-helper); max-width: 34rem; }
.cds-state--error .cds-state__title { color: var(--cds-support-error); }

/*
   Skeleton - the structure of the page, before its data (DESIGN-DOCTRINE §5's
   Feedback set names it; SYSTEM-REVIEW-002's loop pass found it absent).

   Carbon's loading pattern is the reference, per AGENTS.md §3 v9 - the reasoning
   lands here as tokens, the library never lands at all: skeletons for the FIRST
   load of structured content, because they hold the layout the data will occupy
   and the page does not reflow when it arrives; the spinner stays for
   user-triggered work where structure is unknown. One skeleton per surface,
   never one per widget.

   The pulse is opacity, not a colour sweep: it needs no new colour token, so it
   is correct in all ten palette x mode blocks by construction. Duration and
   easing come from the motion scale - calc() over a token is still a token, and
   the reduced-motion sweep above zeroes it like everything else (§10).
*/
.cds-skeleton { display: grid; gap: var(--cds-spacing-04); }

/* Inside a tile the frame is the tile's own; standalone it draws the state frame. */
.cds-skeleton--framed {
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  padding: var(--cds-spacing-05);
}

.cds-skeleton__bar {
  height: 0.75rem;
  border-radius: var(--cds-radius);
  background: var(--cds-layer-accent);
  animation: cds-skeleton-pulse calc(var(--cds-duration-slow-02) * 2)
             var(--cds-ease-standard) infinite alternate;
}

@keyframes cds-skeleton-pulse { from { opacity: 1; } to { opacity: 0.45; } }

/* A table-shaped row: identity column wide, figures narrow, like every register. */
.cds-skeleton__row {
  display: grid;
  grid-template-columns: 2fr 3fr 1fr 1fr;
  gap: var(--cds-spacing-05);
  align-items: center;
  min-height: 2rem;
}

/* Ragged edges, deterministically. Identical bars read as a pattern, not as
   content-to-come; a random width per render would shimmer on every re-render. */
.cds-skeleton__row:nth-child(3n)   .cds-skeleton__bar:first-child { inline-size: 70%; }
.cds-skeleton__row:nth-child(3n+1) .cds-skeleton__bar:nth-child(2) { inline-size: 80%; }
.cds-skeleton__row:nth-child(2n)   .cds-skeleton__bar:last-child  { inline-size: 60%; }

/* The dashboard's opening frame: the KPI strip, held at the KPI bar's own height. */
.cds-skeleton__kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--cds-spacing-04);
}

.cds-skeleton__kpi {
  min-height: 4.5rem;
  border-radius: var(--cds-radius);
  background: var(--cds-layer-accent);
  animation: cds-skeleton-pulse calc(var(--cds-duration-slow-02) * 2)
             var(--cds-ease-standard) infinite alternate;
}

.cds-spinner {
  width: 1.75rem; height: 1.75rem; border-radius: 50%;
  border: 2px solid var(--cds-border-subtle);
  border-top-color: var(--cds-interactive);
  animation: cds-spin 0.9s linear infinite;
}

@keyframes cds-spin { to { transform: rotate(360deg); } }

/* Motion is a courtesy, never a requirement. DESIGN-DOCTRINE §10. */
@media (prefers-reduced-motion: reduce) {
  /*
    The tokens are zeroed as well as the declarations. The sweep below already
    stops every CSS animation and transition, but a duration token is also read
    by JavaScript — a row flash timed off --cds-pos-flash, say — and a token
    that still says 150ms while the stylesheet says 0.01ms is a token that
    lies. §10: reduced means reduced or none, and the UI stays fully
    understandable with all motion removed.
  */
  :root {
    --cds-duration-fast-01: 0ms;
    --cds-duration-fast-02: 0ms;
    --cds-duration-moderate-01: 0ms;
    --cds-duration-moderate-02: 0ms;
    --cds-duration-slow-01: 0ms;
    --cds-duration-slow-02: 0ms;
  }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   PAGER
   ========================================================================== */

.cds-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cds-spacing-04);
  flex-wrap: wrap;
  padding-block: var(--cds-spacing-04);
  font-size: var(--cds-type-02);
  color: var(--cds-text-secondary);
}

.cds-pager__controls { display: flex; align-items: center; gap: var(--cds-spacing-02); }

/* ==========================================================================
   TAGS — flat, square-ish, no border noise
   ========================================================================== */

.cds-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--cds-spacing-02);
  padding-block: 0.0625rem;
  padding-inline: var(--cds-spacing-03);
  border-radius: var(--cds-radius);
  font-size: var(--cds-type-01);
  font-weight: 600;
  background: var(--cds-layer-hover);
  color: var(--cds-text-secondary);
}

.cds-tag--success { background: color-mix(in srgb, var(--cds-support-success) 16%, transparent); color: var(--cds-support-success); }
.cds-tag--error   { background: color-mix(in srgb, var(--cds-support-error) 16%, transparent);   color: var(--cds-support-error); }
.cds-tag--warning { background: color-mix(in srgb, var(--cds-support-warning) 16%, transparent); color: var(--cds-support-warning); }
.cds-tag--info    { background: color-mix(in srgb, var(--cds-support-info) 16%, transparent);    color: var(--cds-support-info); }

/* ==========================================================================
   ICON BUTTON — the one control the shell needs that is not a Radzen component
   ========================================================================== */

.cds-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem; height: 2rem;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: var(--cds-radius);
  color: var(--cds-text-secondary);
  cursor: pointer;
}

.cds-icon-btn:hover { background: var(--cds-layer-hover); color: var(--cds-text-primary); }

/* Touch targets are 44px on POS and mobile (DESIGN-DOCTRINE §11). */
@media (pointer: coarse) {
  .cds-icon-btn { width: 2.75rem; height: 2.75rem; }
}

/* ==========================================================================
   MONEY — separate debit and credit columns, no signs.

   §8.4: ledgers, statements, trial balance and journals use separate Debit /
   Credit columns with no signs at all, matching مدين / دائن. Net figures only
   (balances, totals, ageing) are single signed values, and there negative is
   parentheses — which mirror to )1,500( inside RTL unless isolated, which is
   what .cds-num above is for.
   ========================================================================== */

/* `right`, not `end`: a figure is ltr whatever the page is, so its right-hand end is
   what has to line up. `end` meant one edge on a .cds-num element and the opposite
   edge on a bare one, which is how a heading drifted off its own column. */
.cds-money { text-align: right; white-space: nowrap; }
.cds-money--usd { color: var(--cds-usd); }
.cds-money--iqd { color: var(--cds-iqd); }
.cds-money--negative { color: var(--cds-support-error); }

/* ==========================================================================
   NAVIGATION — group headers and the accordion
   ========================================================================== */

.cds-nav__group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  gap: var(--cds-spacing-03);
  padding-block: var(--cds-spacing-03);
  padding-inline: var(--cds-spacing-05);
  background: transparent;
  border: none;
  border-inline-start: 2px solid transparent;
  color: var(--cds-shell-text);
  font-family: inherit;
  font-size: var(--cds-type-03);
  font-weight: 600;
  text-align: start;
  cursor: pointer;
}

.cds-nav__group:hover { background: var(--cds-shell-layer); color: var(--cds-shell-text-active); }
.cds-nav__group--open { color: var(--cds-shell-text-active); }

/* The chevron points at the content it opens. In RTL that is the mirrored
   direction, which is why it is a rotation and not a different glyph. */
.cds-nav__chevron {
  flex: none;
  transition: transform var(--cds-duration-moderate-01) var(--cds-ease-standard);
}
.cds-nav__group--open .cds-nav__chevron { transform: rotate(180deg); }

.cds-nav__list--nested { padding-block: 0 var(--cds-spacing-03); }
.cds-nav__list--nested .cds-nav__link { padding-inline-start: var(--cds-spacing-07); font-size: var(--cds-type-02); }

/* ==========================================================================
   PALETTE SWATCHES

   Each swatch carries its own data-theme, so the dot renders in the palette it
   selects rather than in the one currently active. That is the whole point of a
   swatch: it has to show you what you would be choosing.
   ========================================================================== */

.cds-palette { display: flex; align-items: center; gap: var(--cds-spacing-02); }

.cds-palette__swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem; height: 1.5rem;
  padding: 0;
  background: transparent;
  border: var(--cds-border-width) solid transparent;
  border-radius: var(--cds-radius);
  cursor: pointer;
}

.cds-palette__swatch:hover { border-color: var(--cds-border-strong); }
.cds-palette__swatch--active { border-color: var(--cds-text-primary); }

.cds-palette__dot {
  width: 0.875rem; height: 0.875rem;
  border-radius: 50%;
  background: var(--cds-interactive);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
}

@media (pointer: coarse) {
  .cds-palette__swatch { width: 2.25rem; height: 2.25rem; }
}

/* ==========================================================================
   SEARCH
   ========================================================================== */

.cds-search { width: 100%; max-width: 22rem; }

/* ==========================================================================
   AUTH — the static-SSR shell
   ========================================================================== */

.cds-auth {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cds-spacing-06);
  padding: var(--cds-spacing-05);
  background: var(--cds-background);
  color: var(--cds-text-primary);
}

.cds-auth__brand { display: flex; align-items: center; gap: var(--cds-spacing-04); }

.cds-auth__mark {
  display: flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem;
  background: var(--cds-interactive);
  color: var(--cds-text-on-color);
  border-radius: var(--cds-radius);
}

.cds-auth__name { font-size: var(--cds-type-05); font-weight: 600; }
.cds-auth__tagline { font-size: var(--cds-type-02); color: var(--cds-text-helper); }

.cds-auth__card {
  width: 100%;
  max-width: 22rem;
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  padding: var(--cds-spacing-06);
}

.cds-auth__note { margin: 0; font-size: var(--cds-type-01); color: var(--cds-text-helper); text-align: center; }

/* ==========================================================================
   RECONNECT + ERROR

   Blazor stamps the state class; nothing here is rendered by a component,
   because by the time it is needed there is no circuit left to render it.
   ========================================================================== */

#components-reconnect-modal { display: none; }

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: var(--cds-z-reconnect);
  align-items: center;
  justify-content: center;
  background: rgb(0 0 0 / 0.62);
  backdrop-filter: blur(2px);
}

.cds-reconnect__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--cds-spacing-04);
  max-width: 24rem;
  padding: var(--cds-spacing-07);
  text-align: center;
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
}

.cds-reconnect__title { margin: 0; font-size: var(--cds-type-05); font-weight: 700; color: var(--cds-text-primary); }
.cds-reconnect__status { margin: 0; font-size: var(--cds-type-03); color: var(--cds-text-secondary); }

.cds-reconnect__btn {
  padding-block: var(--cds-spacing-03);
  padding-inline: var(--cds-spacing-06);
  font-family: inherit;
  font-size: var(--cds-type-03);
  font-weight: 600;
  background: var(--cds-interactive);
  /* Gold, sand and tangerine accents need near-black text. Hardcoding white
     here left this button at ~2:1 in three palettes — unreadable exactly when
     the user is already stuck, which is the worst possible moment for it. */
  color: var(--cds-text-on-color);
  border: none;
  border-radius: var(--cds-radius);
  cursor: pointer;
}

.cds-reconnect__btn:hover { background: var(--cds-interactive-hover); }

.cds-error-ui {
  display: none;
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--cds-z-error);
  padding: var(--cds-spacing-04) var(--cds-spacing-05);
  background: var(--cds-support-error);
  color: #ffffff;
  font-size: var(--cds-type-03);
}

.cds-error-ui .reload,
.cds-error-ui .dismiss { color: #ffffff; margin-inline-start: var(--cds-spacing-04); cursor: pointer; }

/* ==========================================================================
   FORM CONTROLS — the few the static-SSR pages need.

   Interactive screens use Radzen's inputs. Sign-in cannot: it renders with no
   circuit, so there is no component to bind to and these are plain elements.
   ========================================================================== */

.cds-form { display: flex; flex-direction: column; gap: var(--cds-spacing-05); }
.cds-form__title { margin: 0; font-size: var(--cds-type-06); font-weight: 600; }

.cds-form__error {
  margin: 0;
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  font-size: var(--cds-type-03);
  color: var(--cds-support-error);
  background: color-mix(in srgb, var(--cds-support-error) 12%, transparent);
  border-inline-start: 2px solid var(--cds-support-error);
  border-radius: var(--cds-radius);
}

.cds-field { display: flex; flex-direction: column; gap: var(--cds-spacing-02); }
.cds-field__label { font-size: var(--cds-type-02); font-weight: 600; color: var(--cds-text-secondary); }

.cds-field__input {
  width: 100%;
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  font-family: inherit;
  font-size: var(--cds-type-04);
  color: var(--cds-text-primary);
  background: var(--cds-layer-accent);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
}

.cds-field__input:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: 1px; }

.cds-btn {
  padding: var(--cds-spacing-03) var(--cds-spacing-05);
  font-family: inherit;
  font-size: var(--cds-type-03);
  font-weight: 600;
  border: var(--cds-border-width) solid transparent;
  border-radius: var(--cds-radius);
  cursor: pointer;
}

.cds-btn--primary { background: var(--cds-interactive); color: var(--cds-text-on-color); }
.cds-btn--primary:hover { background: var(--cds-interactive-hover); }

@media (pointer: coarse) { .cds-btn { min-height: 2.75rem; } }

/* ==========================================================================
   LIST TOOLBAR — search and filters, above the grid
   ========================================================================== */

.cds-list__toolbar {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-04);
  flex-wrap: wrap;
  margin-block-end: var(--cds-spacing-04);
}

/* The grid sits in a tile and scrolls inside it. Radzen draws its own borders,
   so the tile contributes the outer edge and nothing else. */
.cds-tile .rz-datatable,
.cds-tile .rz-data-grid { border: none; }

.cds-filter { min-width: 12rem; }

/* A heading inside a page, below the page's own h1. */
.cds-section-title {
  font-size: var(--cds-type-05);
  margin-block: var(--cds-spacing-06) var(--cds-spacing-04);
}

/* --cds-link, not --cds-interactive: see the token contract. An interactive
   colour light enough to carry dark text is too light to BE text. */
.cds-link { color: var(--cds-link); text-decoration: none; }
.cds-link:hover { text-decoration: underline; }

/* ==========================================================================
   FORM LAYOUT — two columns above the small breakpoint, one below
   ========================================================================== */

.cds-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--cds-spacing-05);
}

.cds-field__control { width: 100%; }

/*
    A control with a small action beside it - the code box and its regenerate button (D-149).

    align-items: flex-end keeps the button on the box's baseline whatever the control's
    height, and min-width: 0 is what stops the flex item refusing to shrink below the input's
    intrinsic width and pushing the button out of the field.

    No direction rules here on purpose: flex-direction: row follows the document's dir, so the
    button lands after the box in Kurdish and Arabic exactly as it does in English.
*/
.cds-field__paired {
    display: flex;
    align-items: flex-end;
    gap: var(--cds-spacing-03);
}

.cds-field__paired > :first-child { flex: 1 1 auto; min-width: 0; }
.cds-field__paired > :not(:first-child) { flex: 0 0 auto; }

/*
    A static marker on the trailing edge of an input - the currency a price is in (D-151).

    padding-block matches the control's own so the code sits on the number's line rather than
    on the box's top edge, and the helper colour keeps it from competing with the figure: it
    labels the number, it is not part of it.
*/
.cds-field__suffix {
  padding-block: var(--cds-spacing-03);
  font-size: var(--cds-type-01);
  font-weight: 600;
  color: var(--cds-text-helper);
  white-space: nowrap;
}

/* A value that cannot be changed is stated, not disabled. A greyed control invites
   somebody to hunt for the permission that would ungrey it. */
.cds-field__static {
  margin: 0;
  padding-block: var(--cds-spacing-03);
  font-size: var(--cds-type-04);
  color: var(--cds-text-primary);
}

.cds-form-actions {
  display: flex;
  gap: var(--cds-spacing-03);
  margin-block-start: var(--cds-spacing-06);
  padding-block-start: var(--cds-spacing-05);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
}

/* ==========================================================================
   PRINT SHEET — a document that leaves the building

   Hand-built, not a grid. A RadzenDataGrid carries sort arrows, a resizer and a
   pager, and none of those belong on paper; §2 reserves Radzen for grids and
   overlays, and this is the case that shows why the boundary is drawn there.

   One template serves screen and PDF. Playwright renders this same markup, so
   there is no second layout to drift out of step with the first.
   ========================================================================== */

.cds-sheet {
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  padding: var(--cds-spacing-07);
  /* A4 content width. The sheet is the same shape on screen as on paper, so
     nobody is surprised by where it breaks. */
  max-width: 48rem;
  margin-inline: auto;

  /*
     A sheet narrower than its own content scrolls; it does not push the page.

     Every grid container carries .cds-scroll-x and this one never did, because
     a sheet is paper rather than a table. But paper on a 520px screen is still
     narrower than the table printed on it: the price list overflowed by 6px,
     escaped a visible overflow, and made the whole document scroll sideways —
     which on a touch screen is the rubber-banding that makes a page feel broken.

     No cost at full width: past 48rem the content fits and no scrollbar appears.
  */
  overflow-x: auto;
}

.cds-sheet__letterhead {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--cds-spacing-05);
  padding-block-end: var(--cds-spacing-05);
  border-block-end: 2px solid var(--cds-text-primary);
}

.cds-sheet__name { margin: 0; font-size: var(--cds-type-06); font-weight: 700; color: var(--cds-text-primary); }
.cds-sheet__legal { font-size: var(--cds-type-02); color: var(--cds-text-secondary); }
.cds-sheet__contact { font-size: var(--cds-type-01); color: var(--cds-text-helper); line-height: 1.6; text-align: end; }

.cds-sheet__title {
  margin-block: var(--cds-spacing-06) var(--cds-spacing-02);
  font-size: var(--cds-type-05);
  font-weight: 600;
}

/* The date is not decoration. A price list is a snapshot of what is offered today
   (I5: a document line copies the price at entry), so an undated sheet read next
   month is a misrepresentation. */
.cds-sheet__dated { font-size: var(--cds-type-02); color: var(--cds-text-secondary); }

.cds-sheet__note {
  margin-block: var(--cds-spacing-05) 0;
  font-size: var(--cds-type-01);
  color: var(--cds-text-helper);
}

.cds-sheet__table { width: 100%; border-collapse: collapse; margin-block-start: var(--cds-spacing-05); }

.cds-sheet__table th {
  text-align: start;
  padding: var(--cds-spacing-03);
  font-size: var(--cds-type-01);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--cds-text-secondary);
  background: var(--cds-layer-accent);
  border-block-end: var(--cds-border-width) solid var(--cds-border-strong);
  /* A two-word heading that wraps makes one header cell taller than its
     neighbours and the whole rule line steps down mid-table. */
  white-space: nowrap;
}

.cds-sheet__table td {
  padding: var(--cds-spacing-03);
  font-size: var(--cds-type-03);
  color: var(--cds-text-primary);
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
  /* A cell holding a tag beside its text otherwise sits on the tag's baseline
     and the row's text stops being level with itself. */
  vertical-align: middle;
}

/* Tracking one row across a wide statement is the whole difficulty of reading
   one, and it is worse in rtl where the eye starts from the far side. */
.cds-sheet__table tbody tr:hover { background: var(--cds-layer-hover); }

/* A numeric column aligns as ONE declaration, and with a PHYSICAL edge.

   `end` cannot be used here. .cds-num sets direction: ltr so the digits keep their
   own order (§13), so `end` resolves against ltr on any cell wearing .cds-num but
   against the page's rtl on a bare th — the same keyword, two opposite edges. In
   English the split was visible in the product: th.cds-num put the heading right
   while its own figures inherited left, and th.cds-money did the reverse.

   `right` is the correct physical edge in every language precisely because the
   figures are ltr either way: aligning their right-hand end is what stacks units
   over units, tens over tens, the way a ledger column has to read. This is the
   construction system's discipline — one class, both roles, no re-resolution —
   with the edge chosen for the books rather than for the page. */
.cds-sheet__table th.cds-num,
.cds-sheet__table td.cds-num,
.cds-sheet__table th.cds-money,
.cds-sheet__table td.cds-money { text-align: right; }

/* ==========================================================================
   THE GRID WEARS THE SHEET'S CLOTHES

   Two table systems, one product. The sheet above is the record; a
   RadzenDataGrid is the register the same figures are read from all day. Radzen
   ships them at the same 14px with the heading told apart only by its weight
   and no band behind it, so a register and a statement did not look like they
   came from the same application — which is the other half of the report the
   grids read as disorderly.

   Everything below restates the sheet's own header and body values against
   Radzen's class names. Nothing here is a new design; it is the same one,
   said twice because the markup is generated in one case and written in the
   other. Radzen puts the heading's padding on a div inside the th, so the
   typography is set on both the cell and that inner element.
   ========================================================================== */

.rz-grid-table thead th {
  background: var(--cds-layer-accent);
  border-block-end: var(--cds-border-width) solid var(--cds-border-strong);
}

.rz-grid-table thead th,
.rz-grid-table thead th .rz-column-title,
.rz-grid-table thead th .rz-column-title-content {
  font-size: var(--cds-type-01);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--cds-text-secondary);
}

.rz-grid-table tbody td {
  font-size: var(--cds-type-03);
  color: var(--cds-text-primary);
  vertical-align: middle;
}

/* Striping and a hover row fight each other: on a striped table the highlight
   reads as one more stripe rather than as the row under the pointer. The sheet
   has no stripes, so neither does this. */
.rz-grid-table.rz-grid-table-striped tbody tr.rz-data-row,
.rz-grid-table tbody tr.rz-data-row { background: transparent; }
.rz-grid-table tbody tr.rz-data-row:hover { background: var(--cds-layer-hover); }

.cds-sheet__footer {
  margin-block-start: var(--cds-spacing-06);
  padding-block-start: var(--cds-spacing-04);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
  font-size: var(--cds-type-01);
  color: var(--cds-text-helper);
}

/* ==========================================================================
   @media print — everything that is not the document comes off the paper
   ========================================================================== */

@media print {
  /* The shell is navigation. Paper has none. */
  .cds-nav, .cds-appbar, .cds-list__toolbar, .cds-page-header__actions { display: none !important; }

  .cds-shell, .cds-shell__main, .cds-shell__content {
    display: block;
    padding: 0;
    margin: 0;
    max-width: none;
  }

  /* Ink, not screen: the themed surfaces would print as grey blocks and the
     palette a reader happened to choose would decide how their invoice looked. */
  .cds-sheet {
    border: none;
    padding: 0;
    max-width: none;
    background: #ffffff;
  }

  .cds-sheet__table th, .cds-sheet__table td { color: #000000; }
  .cds-sheet__name, .cds-sheet__title { color: #000000; }

  /* The header band is a screen affordance and would print as the grey block
     this section already refuses. The rule under the headings is what separates
     them on paper, and that survives. */
  .cds-sheet__table th { background: transparent; }
  .cds-sheet__table tbody tr:hover { background: transparent; }

  /* A row split across a page break is a row read twice or not at all. */
  .cds-sheet__table tr { break-inside: avoid; }
  .cds-sheet__table thead { display: table-header-group; }

  @page { size: A4; margin: 18mm 16mm; }
}

/* ==========================================================================
   DASHBOARD

   The alarm is not styling. Rule 0: a figure shown without a warning is a figure
   somebody acts on, so a ledger that does not balance says so above everything
   else on the page rather than beside it.
   ========================================================================== */

.cds-alarm {
  margin-block-end: var(--cds-spacing-06);
  padding: var(--cds-spacing-05);
  background: color-mix(in srgb, var(--cds-support-error) 12%, transparent);
  border-inline-start: 3px solid var(--cds-support-error);
  border-radius: var(--cds-radius);
  color: var(--cds-text-primary);
}

.cds-alarm__detail {
  margin-block-start: var(--cds-spacing-02);
  font-size: var(--cds-type-03);
  color: var(--cds-text-secondary);
}

.cds-panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--cds-spacing-05);
}

.cds-panel__title {
  margin: 0 0 var(--cds-spacing-04);
  font-size: var(--cds-type-03);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--cds-text-secondary);
}

.cds-rows { list-style: none; margin: 0; padding: 0; }

.cds-rows__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cds-spacing-04);
  padding-block: var(--cds-spacing-03);
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
  font-size: var(--cds-type-03);
}

.cds-rows__row:last-child { border-block-end: none; }

/* A qualifier trailing a value: a unit after a quantity, a code after a name, a
   Latin rendering after a Sorani one.

   The inline-start margin is load-bearing, not decoration. Two inline elements with
   no whitespace between them render as one word — "کۆمپانیای ئاسۆAso Company" —
   and neither Arabic script nor a script change gives the eye a seam to split on.

   NEVER PUT .cds-num ON THIS ELEMENT. A logical margin resolves against the
   element's OWN direction, and .cds-num sets direction: ltr so that digits and
   codes read left to right. Combine them and margin-inline-start resolves to
   margin-left — the far side from the text it is meant to be spaced away from —
   so the gap appears on the wrong side and the two still touch. Observed here:
   computed marginInlineStart 4px, marginRight 0px, inside an RTL row.

   Nest instead: this class on the outer span, which inherits the row's direction,
   and .cds-num on an inner one, which isolates. Two responsibilities, two
   elements, because one of them changes what the other means. */
.cds-rows__unit {
  margin-inline-start: var(--cds-spacing-02);
  font-size: var(--cds-type-01);
  color: var(--cds-text-helper);

  /*
     THE GAP HAS TO RESOLVE AGAINST THE ROW, NOT AGAINST THIS SPAN.

     These annotations are written with dir="auto" so mixed content reads
     correctly, and dir="auto" on a span holding Latin text resolves that
     span's own direction to ltr. margin-inline-start then means margin-LEFT
     — the outside edge — while in an RTL row the gap is needed on the right.
     Measured at 0px: "سندووق" and "Dinar till" touched, with the 4px sitting
     where nothing could see it.

     direction: inherit takes the row's direction back, so the logical margin
     points at the neighbour again; unicode-bidi: isolate keeps the bidi
     behaviour dir="auto" was there for, which is the half worth keeping.
     Same pair .cds-num already uses, for the same reason.
  */
  direction: inherit;
  unicode-bidi: isolate;
}

/* Hand-built, because §2 reserves Radzen for grids and overlays and a chart is
   neither. Stroke only, matching the icon rule. */
.cds-spark { width: 100%; height: 3.75rem; display: block; }

/* ==========================================================================
   STOCK MOVEMENT QUANTITIES

   The sign is the direction of the movement, so it is never dropped. Colour
   only reinforces what the leading + or - already says: colour alone fails
   WCAG 1.4.1 and fails a printed page, and this register is printed.
   ========================================================================== */

.cds-qty { font-variant-numeric: tabular-nums; font-weight: 600; }
.cds-qty--in  { color: var(--cds-support-success); }
.cds-qty--out { color: var(--cds-support-warning); }
/* ==========================================================================
   COUNT SHEET

   A variance is a direction as well as a number: short means goods are missing,
   over means more arrived than the books know. Both carry a sign, and colour
   only reinforces it — colour alone fails WCAG 1.4.1 and fails on paper.
   ========================================================================== */

.cds-qty--short { color: var(--cds-support-error); }
.cds-qty--over  { color: var(--cds-support-warning); }

/* The settled banner: the same shape as the out-of-balance alarm, in the voice
   of a fact rather than a warning. */
.cds-alarm--settled {
  background: color-mix(in srgb, var(--cds-support-success) 12%, transparent);
  border-inline-start-color: var(--cds-support-success);
}

.cds-form__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--cds-spacing-05);
}

.cds-form__actions {
  display: flex;
  gap: var(--cds-spacing-04);
  margin-block-start: var(--cds-spacing-05);
}

.cds-field__help {
  margin-block-start: var(--cds-spacing-02);
  font-size: var(--cds-type-02);
  color: var(--cds-text-helper);
}

.cds-field__error {
  margin-block-start: var(--cds-spacing-02);
  font-size: var(--cds-type-03);
  color: var(--cds-support-error);
}

.cds-input { width: 100%; }
/* ==========================================================================
   PER-CURRENCY BALANCES

   §21: a party's balances are per currency and are never collapsed into one
   base figure. So the cell holds a list, and the list has to stay legible when
   it holds one row and when it holds four.
   ========================================================================== */

.cds-balances { list-style: none; margin: 0; padding: 0; }

.cds-balances li {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: var(--cds-spacing-02);
}

.cds-check {
  display: inline-flex;
  align-items: center;
  gap: var(--cds-spacing-03);
  font-size: var(--cds-type-03);
  color: var(--cds-text-secondary);
}

/* A secondary verb beside a primary one: still a link, but it must not compete
   with the name it sits next to.

   The margin is not decoration. Two inline elements with no whitespace between
   them render as one word — "بازرگانی ئاسۆدەستکاری" — and in a script without
   capitals there is no visual seam to tell the reader where the name ends and
   the verb begins. Logical, so it sits on the correct side in both directions. */
.cds-link--quiet {
  margin-inline-start: var(--cds-spacing-03);
  font-size: var(--cds-type-02);
  color: var(--cds-text-helper);
}

/* ==========================================================================
   STATEMENT RUNS

   One block per currency. §21 keeps a party's balances per currency, so the
   sheet shows them as separate tables rather than one interleaved column of
   running totals — which is where a reader would otherwise add two currencies
   together by eye.
   ========================================================================== */

.cds-run { margin-block-start: var(--cds-spacing-06); }

.cds-run__title {
  margin: 0 0 var(--cds-spacing-03);
  font-size: var(--cds-type-03);
  font-weight: 700;
  color: var(--cds-text-secondary);
}

.cds-sheet__note {
  margin-block-start: var(--cds-spacing-05);
  font-size: var(--cds-type-02);
  color: var(--cds-text-helper);
}

/* Screen furniture that must not reach the paper. */
@media print {
  .cds-noprint { display: none; }
}


/* An entry that does not sum to zero. Not styling: I3 says this cannot happen,
   so when it does the row has to be impossible to skim past. */
.cds-row--broken {
  background: color-mix(in srgb, var(--cds-support-error) 14%, transparent);
  color: var(--cds-support-error);
}

/* ==========================================================================
   The counter's tile grid (DECISIONS-010 D-056)

   A cashier is standing up, holding goods, with a customer waiting. Everything
   here is sized for a fingertip rather than a pointer, and nothing depends on
   hover — a touch screen has no hover state to reveal anything with.

   Logical properties only, as everywhere: the grid reads right-to-left in ckb
   and ar without a single rule of its own.
   ========================================================================== */

.cds-tiles { margin-block-start: var(--cds-spacing-05); }

.cds-tiles__filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cds-spacing-02);
  margin-block-end: var(--cds-spacing-04);
}

.cds-tile-chip {
  padding: var(--cds-spacing-02) var(--cds-spacing-04);
  font-family: inherit;
  font-size: var(--cds-type-03);
  color: var(--cds-text-secondary);
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  cursor: pointer;
}

.cds-tile-chip--on {
  color: var(--cds-text-on-color);
  background: var(--cds-interactive);
  border-color: var(--cds-interactive);
}

.cds-tiles__grid {
  display: grid;
  /* Auto-fill rather than a fixed count: the same grid serves a phone at the
     counter and a 24-inch till without a breakpoint for each. */
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
  gap: var(--cds-spacing-03);
}

.cds-tile-button {
  display: flex;
  flex-direction: column;
  gap: var(--cds-spacing-02);
  /* 44px is the smallest target a person hits reliably one-handed. These are
     far larger; the floor is here so a dense theme cannot shrink them past it. */
  min-height: 4.5rem;
  padding: var(--cds-spacing-04);
  font-family: inherit;
  text-align: start;
  color: var(--cds-text-primary);
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  cursor: pointer;
}

.cds-tile-button:hover { background: var(--cds-layer-hover); }

.cds-tile-button:active {
  /* The only feedback a touch screen gives is what the page draws itself. */
  background: var(--cds-layer-accent);
}

/*
  THE STOCK-STATE EDGE BAR (§3.1).

  Down the leading edge, so it mirrors with the layout and never moves when a name wraps.
  Absolute rather than a flex child: it must not take a row of its own out of a tile whose
  height is what the page-size measurement is built on.

  Colour is the fast signal and never the only one — the out-of-stock tile is muted and says
  so in words, and a low one prints its quantity (§11).
*/
.cds-tile-button { position: relative; overflow: hidden; }

.cds-tile-button__stock {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 0.25rem;
}

.cds-tile-button__stock--ok { background: var(--cds-support-success); }
.cds-tile-button__stock--low { background: var(--cds-support-warning); }
.cds-tile-button__stock--out { background: var(--cds-support-error); }

/* Visible and muted, never hidden: a cashier hunting a product needs to find it and be told
   it is gone. A tile that vanished would read as a catalogue mistake. */
.cds-tile-button--out { opacity: 0.55; }

.cds-tile-button__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cds-spacing-02);
  margin-block-start: auto;
}

.cds-tile-button__soldout {
  font-size: var(--cds-type-01);
  font-weight: 600;
  color: var(--cds-support-error);
  text-transform: uppercase;
}

.cds-tile-button__low {
  font-size: var(--cds-type-01);
  font-weight: 600;
  color: var(--cds-support-warning);
}

.cds-tile-button__name {
  font-size: var(--cds-type-03);
  font-weight: 600;
  /* Two lines, then ellipsis: a long name must not make one tile twice the
     height of its neighbours and tear a hole in the grid. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.cds-tile-button__price {
  font-size: var(--cds-type-02);
  color: var(--cds-text-secondary);
}

@media (pointer: coarse) {
  .cds-tile-chip { min-height: 2.75rem; }
  .cds-tile-button { min-height: 5.5rem; }
}

/* Product pictures (DECISIONS-010 D-059). */

.cds-product-image {
  inline-size: 12rem;
  margin-block-end: var(--cds-spacing-03);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  overflow: hidden;
}

.cds-product-image img { display: block; inline-size: 100%; block-size: auto; }

/* The thumbnail in the categories list. Same box whether it is a picture or an initial,
   so the column does not change width down the page. */
.cds-cat-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  object-fit: cover;
  border-radius: var(--cds-radius);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-cat-thumb--empty {
  background: var(--cds-layer-accent);
  color: var(--cds-accent);
  font-weight: 600;
}

/*
  The picture's surface. `contain` rather than `cover` on the image inside it, so a cut-out
  keeps its shape and a photograph is not cropped through its own label — the plate supplies
  the rectangle, the picture keeps its proportions.
*/
.cds-tile-button__plate {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 100%;
  block-size: 3.5rem;
  padding: var(--cds-spacing-02);
  background: var(--cds-media-plate);
  border: var(--cds-border-width) solid var(--cds-media-plate-border);
  border-radius: var(--cds-radius);
  overflow: hidden;
}

.cds-tile-button__plate--category {
  inline-size: 3rem;
  block-size: 3rem;
  flex: 0 0 auto;
}

.cds-tile-button__image {
  /* The plate is the fixed box now, so one portrait photograph still cannot make its tile
     taller than the row. `contain` rather than `cover`: cropping was chosen when the image
     WAS the rectangle, and a cut-out cropped to a square loses the shape that identifies it. */
  max-inline-size: 100%;
  max-block-size: 100%;
  inline-size: auto;
  block-size: auto;
  object-fit: contain;
}

/* A chart-of-accounts heading row (DECISIONS-010 D-062).

   Headings group their children and cannot be posted to, so they carry no figures.
   Weighted rather than coloured: the chart is read as a tree, and weight is what
   separates a branch from a leaf without spending a colour that means something
   else everywhere in this design system. */
.cds-row--heading td { font-weight: 600; }

/* A tag sitting after text in a sheet cell needs air before it.

   The margin goes on the tag and resolves against the CELL's direction, which is the
   row's — not against the tag's Latin content. That distinction is the one that put a
   margin on the wrong side once before: a logical property resolves against the
   element's own direction, so anything carrying dir="auto" must not own this spacing. */
.cds-sheet__table td > .cds-tag { margin-inline-start: var(--cds-spacing-03); }

/* ---- Command palette (NAV-IA N6) --------------------------------------------------
   The search that lets the eight-group rail stay eight groups. Sits above everything
   except the reconnect overlay, because a palette under a dialog is a palette nobody
   asked to open. */

.cds-cmd__backdrop {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: var(--cds-z-overlay);
  align-items: flex-start;
  justify-content: center;
  padding-block-start: 12vh;
  background: rgb(0 0 0 / 0.4);
  backdrop-filter: blur(1px);
}

.cds-cmd {
  display: flex;
  flex-direction: column;
  width: min(36rem, calc(100vw - 2 * var(--cds-spacing-05)));
  max-height: 60vh;
  overflow: hidden;
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer);
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.25);
}

.cds-cmd__input {
  padding: var(--cds-spacing-05);
  border: none;
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
  background: transparent;
  color: var(--cds-text-primary);
  font-size: var(--cds-type-05);
}

.cds-cmd__input:focus {
  outline: 2px solid var(--cds-accent);
  outline-offset: -2px;
}

.cds-cmd__list {
  overflow-y: auto;
  padding-block: var(--cds-spacing-02);
}

.cds-cmd__group {
  padding: var(--cds-spacing-03) var(--cds-spacing-05) var(--cds-spacing-01);
  color: var(--cds-text-helper);
  font-size: var(--cds-type-02);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.cds-cmd__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cds-spacing-04);
  width: 100%;
  padding: var(--cds-spacing-03) var(--cds-spacing-05);
  border: none;
  background: transparent;
  color: var(--cds-text-primary);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.cds-cmd__item:hover,
.cds-cmd__item--active {
  background: var(--cds-layer-hover);
}

.cds-cmd__item:focus-visible {
  outline: 2px solid var(--cds-accent);
  outline-offset: -2px;
}

.cds-cmd__detail {
  color: var(--cds-text-helper);
  font-size: var(--cds-type-03);
}

.cds-cmd__empty {
  margin: 0;
  padding: var(--cds-spacing-05);
  color: var(--cds-text-helper);
}

.cds-cmd__hint {
  margin: 0;
  padding: var(--cds-spacing-03) var(--cds-spacing-05);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
  color: var(--cds-text-helper);
  font-size: var(--cds-type-02);
}

/* ==========================================================================
   THE TILL — ScanFlow (POS-UI-CONCEPT.md §1)

   One screen, three zones, and no navigation during a sale. What this
   replaces was a document-entry form wearing POS labels: to sell one item
   for cash a cashier opened a method dropdown, typed an amount, pressed
   "Add tender", then pressed "Ring" — five interactions after the scan,
   three of them dropdowns.

   The model is Odoo's product screen, which is this repository's concept
   reference (D-005, D-032) and is also what counter staff in this market
   have already used: a permanent cart, a tile grid, and ONE NUMPAD WITH
   MODES acting on the selected line. Quantity, price and discount stop
   being three form fields and become three modes of the same ten keys,
   which is why the form disappears rather than merely shrinking.

   Everything is sized from --cds-target-min upward. A cashier taps at
   roughly twice normal speed under queue pressure, often holding the goods
   in the other hand.
   ========================================================================== */

.cds-till {
  /*
    Fills the viewport and never scrolls as a page: each zone scrolls inside
    itself, so the total and the charge button stay where the hand expects
    them however long the basket grows.
  */
  display: grid;
  grid-template-columns: 1fr minmax(22rem, 26rem);
  grid-template-rows: auto 1fr;
  gap: var(--cds-spacing-04);
  block-size: calc(100vh - 9rem);
  min-block-size: 34rem;
}

.cds-till__bar { grid-column: 1 / -1; }

.cds-till__work {
  min-inline-size: 0;
  min-block-size: 0;
}

/*
  Zone C sits at the inline END, and that is what mirrors the layout: under
  dir=rtl the grid lays its columns right-to-left and the cart moves to the
  other side with no rule of its own. Physical left/right would not.
*/
.cds-till__cart {
  position: relative;
  display: flex;
  flex-direction: column;
  min-block-size: 0;
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  box-shadow: var(--cds-elevation-01);
}

/* --- ZONE A — the command bar ------------------------------------------- */

.cds-till__bar {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-04);
  flex-wrap: wrap;
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
}

.cds-till__who { display: flex; flex-direction: column; min-inline-size: 0; }
.cds-till__who strong { font-size: var(--cds-type-04); }
.cds-till__who span { font-size: var(--cds-type-01); color: var(--cds-text-helper); }

.cds-till__bar-actions {
  display: flex;
  gap: var(--cds-spacing-02);
  margin-inline-start: auto;
  flex-wrap: wrap;
}

/*
  Bounded, because .cds-field__control is 100% wide by design everywhere else
  and an unbounded input here pushes the chip and the actions onto a second
  row — which is how a 64px command bar silently became 135px.
*/
.cds-till__scan {
  inline-size: auto;
  max-inline-size: 18rem;
  flex: 1 1 12rem;
}

/*
  The scan-state chip. Production is remote (I9 v2), so a scan round-trips a
  WAN; the chip is what makes the confirmation feel immediate anyway.
*/
.cds-till__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--cds-spacing-02);
  padding: var(--cds-spacing-02) var(--cds-spacing-03);
  border-radius: var(--cds-radius);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  font-size: var(--cds-type-02);
  color: var(--cds-text-secondary);
  white-space: nowrap;
}

.cds-till__chip::before {
  content: "";
  inline-size: 0.5rem;
  block-size: 0.5rem;
  border-radius: 50%;
  background: var(--cds-pos-stock-ok);
}

/*
  Flashed by the shell listener at CAPTURE time, before the circuit answers. It reports that
  the scan was heard, which is the honest claim and the only one available before the round
  trip completes — see pos-shell.js.
*/
@keyframes cds-till-heard {
  from { background: var(--cds-accent); color: var(--cds-text-on-color); }
  to { background: transparent; }
}

.cds-till__chip--flash {
  animation: cds-till-heard var(--cds-pos-slide) var(--cds-ease-exit);
}

/* --- ZONE C — the cart --------------------------------------------------- */

.cds-till__lines {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  margin: 0;
  padding: 0;
  list-style: none;
}

/*
  A cart line is a BUTTON, not a row with buttons in it. The whole line is
  the target, and selecting it is what the numpad then acts on — the
  interaction the old form had no equivalent of.
*/
.cds-till__line {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 var(--cds-spacing-03);
  inline-size: 100%;
  min-block-size: var(--cds-target-min);
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  border: 0;
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
  background: transparent;
  color: var(--cds-text-primary);
  text-align: start;
  font: inherit;
  cursor: pointer;
}

.cds-till__line:hover { background: var(--cds-layer-hover); }
.cds-till__line:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: -2px; }

/*
  Selection is a bar on the inline-start edge plus a background — never
  colour alone (§11) — and the bar is a logical border, so it moves with the
  reading direction instead of stranding itself on the left in Sorani.
*/
.cds-till__line--on {
  background: var(--cds-layer-accent);
  box-shadow: inset 3px 0 0 0 var(--cds-accent);
}

.cds-till__line-name { font-size: var(--cds-type-03); font-weight: 600; }
.cds-till__line-total { font-size: var(--cds-type-03); font-weight: 600; }

.cds-till__line-meta {
  grid-column: 1 / -1;
  font-size: var(--cds-type-01);
  color: var(--cds-text-helper);
}

/* The flagship motion (§10): 150ms, confirming a scan peripherally. */
@keyframes cds-till-flash {
  from { background: var(--cds-layer-hover); }
  to { background: transparent; }
}

.cds-till__line--rung {
  animation: cds-till-flash var(--cds-pos-flash) var(--cds-ease-exit);
}

.cds-till__empty {
  padding: var(--cds-spacing-07) var(--cds-spacing-05);
  text-align: center;
  color: var(--cds-text-helper);
  font-size: var(--cds-type-03);
}

/* --- the totals block, and the one number that dominates ----------------- */

.cds-till__totals {
  flex: none;
  padding: var(--cds-spacing-04);
  border-block-start: var(--cds-border-width) solid var(--cds-border-strong);
}

.cds-till__sub {
  display: flex;
  justify-content: space-between;
  gap: var(--cds-spacing-03);
  font-size: var(--cds-type-02);
  color: var(--cds-text-secondary);
  padding-block-end: var(--cds-spacing-02);
}

/*
  §4.1: "TOTAL is the largest text on screen." Not a heading, not a strong
  tag in a list — the largest thing rendered, at --cds-type-08, a step that
  exists for this and nothing else.
*/
.cds-till__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cds-spacing-03);
  padding-block: var(--cds-spacing-02) var(--cds-spacing-03);
}

.cds-till__total-label {
  font-size: var(--cds-type-02);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--cds-text-helper);
}

.cds-till__total-value {
  font-size: var(--cds-pos-total-size);
  font-weight: 700;
  line-height: 1;
  color: var(--cds-text-primary);
}

.cds-till__total-alt {
  display: flex;
  justify-content: flex-end;
  font-size: var(--cds-type-02);
  color: var(--cds-text-helper);
  padding-block-end: var(--cds-spacing-03);
}

/* The one accent button. Nothing else on this screen is this size. */
.cds-till__charge {
  inline-size: 100%;
  min-block-size: 4rem;
  border: 0;
  border-radius: var(--cds-radius);
  background: var(--cds-accent);
  color: var(--cds-text-on-color);
  font-size: var(--cds-type-06);
  font-weight: 700;
  cursor: pointer;
}

.cds-till__charge:disabled { opacity: 0.45; cursor: not-allowed; }
.cds-till__charge:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: 2px; }

/* --- the numpad: three modes, ten keys ----------------------------------- */

.cds-till__pad {
  flex: none;
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-till__modes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--cds-spacing-02);
  padding-block-end: var(--cds-spacing-02);
}

.cds-till__mode {
  min-block-size: var(--cds-target-min);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer);
  color: var(--cds-text-secondary);
  font-size: var(--cds-type-02);
  font-weight: 600;
  cursor: pointer;
}

/* The chosen mode carries a fill and aria-pressed, never colour alone. */
.cds-till__mode--on {
  background: var(--cds-accent);
  border-color: var(--cds-accent);
  color: var(--cds-text-on-color);
}

.cds-till__mode:disabled { opacity: 0.45; cursor: not-allowed; }

.cds-till__keys {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--cds-spacing-02);
}

.cds-till__key {
  min-block-size: 3rem;
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer);
  color: var(--cds-text-primary);
  font-family: var(--cds-font-numeric);
  font-size: var(--cds-type-06);
  cursor: pointer;
}

.cds-till__key:hover { background: var(--cds-layer-hover); }
.cds-till__key:active { background: var(--cds-layer-accent); }
.cds-till__key:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: -2px; }
.cds-till__key:disabled { opacity: 0.4; cursor: not-allowed; }
.cds-till__key--soft { font-size: var(--cds-type-03); color: var(--cds-text-secondary); }
.cds-till__key--danger { color: var(--cds-support-error); }

/* --- ZONE B — the work surface ------------------------------------------- */

.cds-till__panel {
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  padding: var(--cds-spacing-04);
}

/* --- the payment overlay: over Zone C only (§4.2) ------------------------ */

.cds-till__pay {
  position: absolute;
  inset: 0;
  z-index: var(--cds-z-overlay);
  display: flex;
  flex-direction: column;
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-strong);
  border-radius: var(--cds-radius);
  box-shadow: var(--cds-elevation-03);
  animation: cds-till-slide var(--cds-pos-slide) var(--cds-ease-entrance);
}

@keyframes cds-till-slide {
  from { transform: translateY(1rem); opacity: 0; }
  to { transform: none; opacity: 1; }
}

.cds-till__pay-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cds-spacing-03);
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-till__pay-head h2 { margin: 0; font-size: var(--cds-type-04); }

.cds-till__pay-body {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  padding: var(--cds-spacing-04);
}

.cds-till__tenders {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--cds-spacing-02);
  padding-block-end: var(--cds-spacing-03);
}

.cds-till__tender {
  min-block-size: 3rem;
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer);
  color: var(--cds-text-primary);
  font-size: var(--cds-type-03);
  font-weight: 600;
  cursor: pointer;
}

.cds-till__tender--on {
  background: var(--cds-accent);
  border-color: var(--cds-accent);
  color: var(--cds-text-on-color);
}

/*
  Denomination presets, computed from the real remaining balance and the
  policy's own note step — never a hardcoded 10/20/50 (§4.2).
*/
.cds-till__notes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5rem, 1fr));
  gap: var(--cds-spacing-02);
  padding-block-end: var(--cds-spacing-03);
}

.cds-till__note {
  min-block-size: var(--cds-target-min);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer-accent);
  color: var(--cds-text-primary);
  font-family: var(--cds-font-numeric);
  font-size: var(--cds-type-03);
  font-weight: 600;
  cursor: pointer;
}

.cds-till__note:hover { background: var(--cds-layer-hover); }

/*
  Remaining-due is the single source of truth (§4.2) and the largest thing
  in the overlay, for the reason TOTAL is the largest thing on the screen:
  it is the number that decides whether the customer may leave.
*/
.cds-till__due {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cds-spacing-03);
  padding-block: var(--cds-spacing-03);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-till__due-label { font-size: var(--cds-type-02); color: var(--cds-text-helper); }

.cds-till__due-value {
  font-size: var(--cds-type-07);
  font-weight: 700;
  line-height: 1;
}

.cds-till__due--settled .cds-till__due-value { color: var(--cds-support-success); }
.cds-till__due--owing .cds-till__due-value { color: var(--cds-support-warning); }

.cds-till__legs { margin: 0; padding: 0; list-style: none; }

.cds-till__leg {
  display: flex;
  justify-content: space-between;
  gap: var(--cds-spacing-03);
  align-items: center;
  min-block-size: var(--cds-target-min);
  font-size: var(--cds-type-02);
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-till__pay-foot {
  flex: none;
  padding: var(--cds-spacing-04);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
}

/*
  A phone is not a till, but a small counter screen is. Below this the zones
  stack and the cart leads, because the basket is what a cashier looks at.
*/
@media (max-width: 60rem) {
  .cds-till {
    grid-template-columns: 1fr;
    block-size: auto;
    min-block-size: 0;
  }

  .cds-till__cart { order: -1; }
}

/* --- ZONE B: the two browse levels (§3.1) -------------------------------- */

.cds-till__crumbs {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-02);
  flex-wrap: wrap;
  padding-block-end: var(--cds-spacing-03);
}

/* Mirrors with the text: a chevron between crumbs points the reading way. */
.cds-till__crumb-sep { color: var(--cds-text-helper); }
[dir="rtl"] .cds-till__crumb-sep { transform: scaleX(-1); display: inline-block; }

/*
  A category tile is taller than a product tile and carries no price, so the two
  levels do not read as the same grid with different words in it.
*/
.cds-tile-button--category {
  /* Centred within its row, which is now the row height rather than whatever height was
     going spare. */
  justify-content: center;
  align-items: center;
  gap: var(--cds-spacing-02);
  text-align: center;
}

/* A category's picture takes exactly the room the initials block would have, so a grid of
   some-with and some-without does not come out ragged. `cover` rather than `contain` here:
   a category picture is a scene rather than a labelled box, and cropping one reads better
   than floating it in empty space. */
/* The plate carries the box for a category picture too, so both levels of the grid treat a
   picture the same way — one of them cropping and the other not was a difference nobody
   chose. */
.cds-till__grid .cds-tile-button--category .cds-tile-button__plate--category {
  margin-inline: auto;
}

/*
  The initials block that stands in for a picture. §3.1: "image or generated
  initials block (never a broken-image icon)". Since D-140 a category can have
  a real picture; this is what the ones without get, which on the first day of
  a shop is all of them.
*/
.cds-till__cat-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 3rem;
  block-size: 3rem;
  border-radius: var(--cds-radius);
  background: var(--cds-layer-accent);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  color: var(--cds-accent);
  font-size: var(--cds-type-06);
  font-weight: 700;
  line-height: 1;
}

/* --- the pager ----------------------------------------------------------- */

.cds-till__pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--cds-spacing-04);
  padding-block-start: var(--cds-spacing-04);
}

/*
  Two big targets rather than a row of page numbers. A cashier is holding goods
  in the other hand, and a 44px floor applies here as everywhere (§11).
*/
.cds-till__page {
  min-inline-size: 3.5rem;
  min-block-size: var(--cds-target-min);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer);
  color: var(--cds-text-primary);
  font-size: var(--cds-type-06);
  line-height: 1;
  cursor: pointer;
}

.cds-till__page:hover:not(:disabled) { background: var(--cds-layer-hover); }
.cds-till__page:disabled { opacity: 0.35; cursor: not-allowed; }
.cds-till__page:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: 2px; }

/* The arrows point the reading way, like every other directional glyph here. */
[dir="rtl"] .cds-till__page { transform: scaleX(-1); }

.cds-till__page-count {
  font-size: var(--cds-type-03);
  color: var(--cds-text-secondary);
}

/* --- THE ZONE THAT DOES NOT SCROLL (owner, 2026-08-22) ------------------- */

/*
  "When we have pagination we don't need vertical scroll visible."

  Right, and it goes further than hiding a scrollbar: the grid is sized to
  FILL its zone in a fixed five-by-four, and the page size is twenty to match.
  The page and the grid are one decision — pick a count the grid cannot show
  and the zone starts scrolling again, which is the thing the pager replaced.

  Tiles stretch to fill rather than sitting at a fixed height, so a page of
  four items and a page of twenty both look deliberate instead of leaving a
  ragged gap at the bottom.
*/
.cds-till__work {
  display: flex;
  flex-direction: column;
  gap: var(--cds-spacing-03);
  overflow: hidden;          /* was auto: nothing here scrolls any more */
}

.cds-till__panel {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-block-size: 0;
}

/* Search and breadcrumbs hold their height; the grid takes everything left. */
.cds-till__panel-head { flex: none; }

.cds-till__grid {
  flex: 1 1 auto;
  min-block-size: 0;
  display: grid;
  /*
    The COLUMNS are the browser's decision, from a minimum tile width that a
    two-line product name can actually be read in. Five fixed columns was the
    first attempt and it was wrong the moment the zone got narrow: at 1080px
    the tiles came out 64px wide holding 219px names, and seventeen of twenty
    overflowed their own tile.

    The row count then follows from the height, and the PAGE SIZE follows from
    both - measured in the browser and sent back, so a page is always exactly
    what the grid can show. That is what keeps the zone from scrolling at any
    window size, including fullscreen.
  */
  grid-template-columns: repeat(auto-fill, minmax(var(--cds-till-tile), 1fr));

  /*
    ROWS ARE THE NOMINAL HEIGHT, NOT 1fr.

    They were minmax(row, 1fr), and 1fr means "share whatever height is left". With a full
    page of products that is invisible - the page size is computed to fill the zone, so the
    rows come out about the nominal height anyway. With a HANDFUL of tiles it is not: the
    category level has two, which is one row, and that one row absorbed the entire zone. A
    category card six hundred pixels tall with a small picture adrift in the middle of it,
    which is what the counter reported.

    Nothing is lost by fixing them, because the capacity measurement never trusted the
    rendered height in the first place - pos-shell.js reads the token and says why: rows
    were 1fr, so measuring what was on screen chased its own tail. The measurement and the
    layout now agree about what a row is instead of one compensating for the other.

    align-content: start so the leftover height collects at the bottom of the zone rather
    than being shared out between the rows, which is the same stretch by another name.
  */
  grid-auto-rows: var(--cds-till-row);
  align-content: start;
  gap: var(--cds-spacing-03);
}

/*
  The nominal tile. Both numbers are read back by the capacity measurement, so
  the grid and the page size cannot disagree about what a tile is.

  The row minimum is what breaks a feedback loop that the first version had:
  rows were sized 1fr with no floor, so ten tiles became two rows of 343px,
  and measuring THAT height concluded only two rows fit — which kept the page
  at ten. Sizing from a nominal height instead of the rendered one is what
  makes the answer stable.
*/
:root {
  --cds-till-tile: 9rem;
  --cds-till-row: 8rem;

  /*
    THE PLATE A PRODUCT PICTURE SITS ON.

    Declared here and NOT re-declared in any palette or dark block, and that is the whole
    point of it rather than an oversight: product photography is shot on white and arrives
    as a cut-out as often as not, so the surface behind it has to be the one the picture
    was made for. A plate that followed the theme would put a dark backdrop behind every
    cut-out the moment somebody switched palette — which is the fault that was reported,
    arriving by a different route.

    Very slightly off pure white so the plate reads as a surface against a white tile
    rather than disappearing into it.
  */
  --cds-media-plate: #fbfbfd;
  --cds-media-plate-border: rgb(0 0 0 / 8%);
}

/*
  Four rows only when there is enough height to give them. Below that the grid
  keeps its columns and lets the rows size themselves, which is the honest
  behaviour on a short window: fewer, taller tiles beat clipped ones.
*/


/* A stretched tile: the content sits centred rather than pinned to the top. */
.cds-till__grid .cds-tile-button {
  block-size: 100%;
  min-block-size: var(--cds-target-min);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cds-spacing-02);
}

.cds-till__grid .cds-tile-button__name {
  min-inline-size: 0;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

.cds-till__grid .cds-tile-button__plate {
  /* The picture takes at most this much of the tile, so the name and the price always have
     room. A share rather than a fixed height: the tile is sized from a token. */
  block-size: 45%;
  flex: 0 0 auto;
}

/* Held baskets, as chips in the header — see the markup for why not a panel. */
.cds-till__held { display: inline-flex; gap: var(--cds-spacing-02); }

/* A select bar, sized for a fingertip rather than a pointer. */
.cds-till__pick {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-03);
  flex-wrap: wrap;
  padding-block-end: var(--cds-spacing-02);
}

.cds-till__pick .rz-selectbar .rz-button { min-block-size: var(--cds-target-min); }

/* --- scrollbars, hidden but not disabled --------------------------------- */

/*
  The cart still scrolls — a basket can be longer than the screen and there is
  no pager for it — but the chrome goes. On a touch till a scrollbar is a
  4px-wide mouse target nobody aims at, and its absence is not a loss because
  the content moves under the finger.

  Function is untouched: keyboard, wheel and touch all still scroll.
*/
.cds-till__lines {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.cds-till__lines::-webkit-scrollbar { inline-size: 0; block-size: 0; }

.cds-till__pay-body {
  scrollbar-width: thin;
}

/* --- the fullscreen button ----------------------------------------------- */

.cds-till__full {
  display: inline-flex;
  align-items: center;
  gap: var(--cds-spacing-02);
  min-block-size: var(--cds-target-min);
  padding-inline: var(--cds-spacing-03);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: var(--cds-layer);
  color: var(--cds-text-secondary);
  font: inherit;
  font-size: var(--cds-type-02);
  cursor: pointer;
}

.cds-till__full:hover { background: var(--cds-layer-hover); }
.cds-till__full:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: 2px; }

.cds-till__full[aria-pressed="true"] {
  background: var(--cds-accent);
  border-color: var(--cds-accent);
  color: var(--cds-text-on-color);
}

/* The label is the first thing to go when the bar runs out of room. */
@media (max-width: 90rem) {
  .cds-till__full-text { display: none; }
}

/*
  In fullscreen the app shell is still on screen, so the till claims the height
  the browser chrome gave back rather than leaving a band of empty page.
*/
:fullscreen .cds-till { block-size: calc(100vh - 7rem); }

/* --- the sale result, where the basket was ------------------------------- */

.cds-till__done {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--cds-spacing-02);
  padding: var(--cds-spacing-07) var(--cds-spacing-04);
  text-align: center;
}

.cds-till__done-label {
  font-size: var(--cds-type-02);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--cds-text-helper);
}

/* The size the total had a second ago: it is the figure the hand needs next. */
.cds-till__done-value {
  font-size: var(--cds-pos-total-size);
  font-weight: 700;
  line-height: 1;
  color: var(--cds-support-success);
}

.cds-till__done-cur { font-size: var(--cds-type-02); color: var(--cds-text-helper); }

.cds-till__done-doc {
  font-size: var(--cds-type-02);
  color: var(--cds-text-secondary);
  padding-block-start: var(--cds-spacing-03);
}

/*
  Stacked layout keeps its scroll: below this width the zones are one column
  and the page is the scroller, which is correct on a small screen.
*/
@media (max-width: 60rem) {
  .cds-till__work { overflow: visible; }
  .cds-till__grid { grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); grid-template-rows: none; }
  .cds-till__grid .cds-tile-button { block-size: auto; min-block-size: 6rem; }
}


/* --- the journal's receipt rows (§6.1) ----------------------------------- */

.cds-till__receipt {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-02);
  border-block-end: var(--cds-border-width) solid var(--cds-border-subtle);
}

/*
  The row is the target, like a cart line: tapping anywhere on it opens the
  receipt. The return button sits outside that target rather than inside it,
  because a mis-tap that starts a refund is not a mis-tap anybody wants.
*/
.cds-till__receipt-main {
  flex: 1 1 auto;
  min-inline-size: 0;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 var(--cds-spacing-03);
  min-block-size: var(--cds-target-min);
  padding: var(--cds-spacing-03) var(--cds-spacing-02);
  border: 0;
  background: transparent;
  color: var(--cds-text-primary);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.cds-till__receipt-main:hover { background: var(--cds-layer-hover); }
.cds-till__receipt-main:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: -2px; }

.cds-till__receipt-no { font-size: var(--cds-type-03); font-weight: 600; }
.cds-till__receipt-total { font-size: var(--cds-type-03); font-weight: 600; }

.cds-till__receipt-meta {
  grid-column: 1 / -1;
  font-size: var(--cds-type-01);
  color: var(--cds-text-helper);
}

/*
  Status is a WORD first. The colour is a second signal on top of it, never
  the only one (§11) - a returned receipt and a voided one must be tellable
  apart by somebody who cannot distinguish amber from red.
*/
.cds-till__receipt-state {
  grid-column: 1 / -1;
  justify-self: start;
  font-size: var(--cds-type-01);
  font-weight: 600;
}

.cds-till__receipt-state--completed { color: var(--cds-support-success); }
.cds-till__receipt-state--partiallyreturned { color: var(--cds-support-warning); }
.cds-till__receipt-state--returned { color: var(--cds-text-helper); }
.cds-till__receipt-state--voided { color: var(--cds-support-error); }


/* --- price check (§6.8): an answer, never a sale -------------------------- */

/*
  Given the accent border so the mode is legible from across the counter. A
  cashier who forgets they are in price-check mode will scan an item and
  wonder why the basket did not move; the surface has to say which mode it
  is in without being read.
*/
.cds-till__price {
  background: var(--cds-layer);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-inline-start: 3px solid var(--cds-accent);
  border-radius: var(--cds-radius);
  padding: var(--cds-spacing-04);
}

.cds-till__price-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cds-spacing-04);
}

.cds-till__price-name { font-size: var(--cds-type-05); font-weight: 600; }

.cds-till__price-value {
  font-size: var(--cds-type-07);
  font-weight: 700;
  line-height: 1;
}

.cds-till__price-cur {
  font-size: var(--cds-type-02);
  color: var(--cds-text-helper);
  padding-block-end: var(--cds-spacing-03);
}

.cds-till__stock { margin: 0; padding: 0; list-style: none; }

.cds-till__stock-row {
  display: flex;
  justify-content: space-between;
  gap: var(--cds-spacing-03);
  align-items: center;
  min-block-size: var(--cds-target-min);
  font-size: var(--cds-type-03);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
}

/* Muted AND labelled - the row says "none" in words as well (§11). */
.cds-till__stock-row--none { color: var(--cds-text-helper); }

/*
  Oversold is not empty. A warehouse below zero means the books and the shelf
  disagree, which a cashier needs to see as a problem rather than as an empty
  shelf - so it takes the error colour and its own word, not the muted grey.
*/
.cds-till__stock-row--short { color: var(--cds-support-error); font-weight: 600; }


/*
  The printer chip in trouble (§5.3). It reports the QUEUE rather than a
  socket: nothing on this deployment can reach a printer directly (I9 v2), so
  "ready" would be a claim the server cannot make. What it can say honestly is
  how many receipts are still owed, and whether any attempt has failed.
*/
.cds-till__chip--bad {
  border-color: var(--cds-support-error);
  color: var(--cds-support-error);
  font-weight: 600;
}

.cds-till__chip--bad::before { background: var(--cds-support-error); }

/* ============================================================================
   POS HARDWARE SETTINGS (POS-UI-CONCEPT.md §5.1, §6.5)

   A back-office screen, so it uses the ordinary form tokens rather than the
   till's oversized touch ones — it is read on a desk with a mouse, not thumbed
   at a counter while somebody waits.
   ========================================================================= */

.cds-devices__state {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-03);
  margin: 0 0 var(--cds-spacing-03);
  font-size: var(--cds-type-03);
  color: var(--cds-text-primary);
}

/* A light, because "is it running" is a yes/no and a sentence makes you read
   to find that out. The text beside it carries the detail. */
.cds-devices__dot {
  inline-size: 0.75rem;
  block-size: 0.75rem;
  border-radius: 50%;
  flex: 0 0 auto;
}

.cds-devices__dot--on {
  background: var(--cds-support-success);
}

/* Grey, not red. A relay that has never been installed is not a fault — it is
   a counter nobody has set up yet, and alarming somebody about it teaches them
   to ignore the colour. */
.cds-devices__dot--off {
  background: var(--cds-border-strong);
}

.cds-devices__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cds-spacing-04);
}

/* No Sale, inside the till's drawer panel. Set apart from Paid In and Paid Out
   above the rule, because it is the one drawer action that moves no money. */
.cds-till__nosale {
  display: flex;
  flex-direction: column;
  gap: var(--cds-spacing-02);
  padding-block-end: var(--cds-spacing-05);
  margin-block-end: var(--cds-spacing-05);
  border-block-end: 1px solid var(--cds-border-subtle);
}

.cds-till__nosale-note {
  margin: 0;
  font-size: var(--cds-type-01);
  color: var(--cds-text-secondary);
}

/* The printers a counter reported, listed rather than only offered. Somebody who
   cannot find their printer needs to see what the machine actually has. */
.cds-devices__found {
  margin: var(--cds-spacing-03) 0 var(--cds-spacing-02);
  font-size: var(--cds-type-03);
  color: var(--cds-text-secondary);
}

.cds-devices__list {
  margin: 0 0 var(--cds-spacing-03);
  padding-inline-start: var(--cds-spacing-05);
  font-size: var(--cds-type-03);
  color: var(--cds-text-primary);
}

.cds-devices__list li {
  padding-block: 0.125rem;
}

.cds-devices__chosen {
  margin-inline-start: var(--cds-spacing-03);
  font-size: var(--cds-type-01);
  color: var(--cds-support-success);
}

/* The checklist shown when no relay has reported. Numbered, because it is meant to be
   worked through in order. */
.cds-devices__steps {
  margin: var(--cds-spacing-03) 0 0;
  padding-inline-start: var(--cds-spacing-06);
  font-size: var(--cds-type-03);
  color: var(--cds-text-secondary);
  max-width: 40rem;
}

.cds-devices__steps li {
  padding-block: 0.1875rem;
}

.cds-devices__probe {
  max-width: 34rem;
}

/* The reading from one scanned burst. Bordered on the leading edge in the verdict's
   colour, so accepted and rejected are distinguishable before a word is read. */
.cds-devices__reading {
  margin-block-start: var(--cds-spacing-03);
  padding: var(--cds-spacing-04);
  border-inline-start: 3px solid var(--cds-border-strong);
  background: var(--cds-layer-accent);
}

.cds-devices__reading--ok {
  border-inline-start-color: var(--cds-support-success);
}

.cds-devices__reading--bad {
  border-inline-start-color: var(--cds-support-error);
}

.cds-devices__verdict {
  margin: 0 0 var(--cds-spacing-03);
  font-size: var(--cds-type-04);
  color: var(--cds-text-primary);
}

/* Two columns: what was measured, and the measurement. A table would be heavier for
   four facts that are read as pairs. */
.cds-devices__facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--cds-spacing-02) var(--cds-spacing-05);
  margin: 0;
  font-size: var(--cds-type-03);
}

.cds-devices__facts dt {
  color: var(--cds-text-secondary);
}

.cds-devices__facts dd {
  margin: 0;
  color: var(--cds-text-primary);
  overflow-wrap: anywhere;
}

.cds-devices__why {
  margin: var(--cds-spacing-03) 0 0;
  font-size: var(--cds-type-03);
  color: var(--cds-text-primary);
}

/* ============================================================================
   THE USER MENU (SYSTEM-REVIEW-001 R-01)

   Carbon's shell anchors a panel to a header icon. The panel is placed with
   logical properties throughout — inset-inline-end, not right — because this
   application runs right-to-left in two of its three languages and a panel
   pinned to the physical right would fall off the wrong edge in Sorani.
   ========================================================================= */

.cds-usermenu {
  position: relative;
  display: flex;
  align-items: center;
}

.cds-usermenu__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2rem;
  block-size: 2rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--cds-interactive);
  color: #fff;
  font-size: var(--cds-type-02);
  font-weight: 600;
  cursor: pointer;
}

.cds-usermenu__trigger:hover { filter: brightness(1.1); }

.cds-usermenu__trigger:focus-visible {
  outline: 2px solid var(--cds-focus);
  outline-offset: 2px;
}

/* Covers the viewport so a click anywhere closes the panel. Transparent, and
   beneath the panel — a JavaScript document listener would do the same job and
   would need tearing down when the circuit drops. */
.cds-usermenu__scrim {
  position: fixed;
  inset: 0;
  /* Above the app bar it hangs from, below anything that owns the whole screen.
     A palette or a POS overlay must cover this, not sit under it. */
  z-index: var(--cds-z-nav);
  border: none;
  padding: 0;
  background: transparent;
  cursor: default;
}

.cds-usermenu__panel {
  position: absolute;
  inset-block-start: calc(100% + 0.5rem);
  inset-inline-end: 0;
  /* One above the scrim so the panel is clickable through its own backdrop.
     calc rather than a second token: this is a relationship between two rules in
     the same component, not a new layer in the contract. */
  z-index: calc(var(--cds-z-nav) + 1);
  min-inline-size: 15rem;
  padding: var(--cds-spacing-05);
  background: var(--cds-layer);
  border: 1px solid var(--cds-border-subtle);
  box-shadow: 0 2px 12px rgb(0 0 0 / 18%);
}

.cds-usermenu__head {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding-block-end: var(--cds-spacing-04);
  border-block-end: 1px solid var(--cds-border-subtle);
}

.cds-usermenu__name {
  font-size: var(--cds-type-04);
  color: var(--cds-text-primary);
}

.cds-usermenu__roles {
  font-size: var(--cds-type-01);
  color: var(--cds-text-secondary);
}

.cds-usermenu__facts {
  display: flex;
  justify-content: space-between;
  gap: var(--cds-spacing-05);
  margin: 0;
  padding-block: var(--cds-spacing-04);
  font-size: var(--cds-type-03);
}

.cds-usermenu__facts dt { color: var(--cds-text-secondary); }
.cds-usermenu__facts dd { margin: 0; color: var(--cds-text-primary); }

/* The language switcher, when the business has more than one interface language.
   A row of buttons rather than a select: two or three items, each one POST. */
.cds-usermenu__languages {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cds-spacing-02);
  padding-block: var(--cds-spacing-03);
  border-block-start: var(--cds-border-width) solid var(--cds-border-subtle);
}

.cds-usermenu__languages-label {
  inline-size: 100%;
  color: var(--cds-text-helper);
  font-size: var(--cds-type-01);
}

.cds-usermenu__language {
  padding: var(--cds-spacing-02) var(--cds-spacing-03);
  border: var(--cds-border-width) solid var(--cds-border-subtle);
  border-radius: var(--cds-radius);
  background: transparent;
  color: var(--cds-text-primary);
  font: inherit;
  cursor: pointer;
}

.cds-usermenu__language:hover { background: var(--cds-layer-hover); }

/* The one being read. aria-current carries it for a screen reader; this is the same fact
   said visually, and not by colour alone. */
.cds-usermenu__language--current {
  border-color: var(--cds-accent);
  background: var(--cds-layer-accent);
  font-weight: 600;
}

.cds-usermenu__out {
  margin: 0;
  padding-block-start: var(--cds-spacing-04);
  border-block-start: 1px solid var(--cds-border-subtle);
}

/* Full width and plainly labelled. Ending a session is not a destructive act
   that needs a red button — it is the ordinary way to finish a shift, and
   colouring it as a danger teaches people to hesitate over the safe thing. */
.cds-usermenu__signout {
  inline-size: 100%;
  padding: var(--cds-spacing-03) var(--cds-spacing-04);
  border: 1px solid var(--cds-border-subtle);
  background: transparent;
  color: var(--cds-text-primary);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.cds-usermenu__signout:hover { background: var(--cds-layer-hover); }

.cds-usermenu__signout:focus-visible {
  outline: 2px solid var(--cds-focus);
  outline-offset: -2px;
}

/* The business logo on the settings screen (SYSTEM-REVIEW-001 R-04). */
.cds-logo {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-05);
  flex-wrap: wrap;
  margin-block-start: var(--cds-spacing-04);
}

/* Contained, not cropped. A letterhead is whatever shape the business had made,
   and cover would cut the edge off a wide one. */
.cds-logo__image {
  max-inline-size: 12rem;
  max-block-size: 5rem;

  /* The same box as the empty state, so the panel does not change height between
     "no logo" and "a logo". A 1x1 placeholder rendered at 17px looked like a broken
     image rather than a small one; contain letterboxes it instead of stretching it,
     which is the honest presentation of an image that really is tiny. */
  min-inline-size: 12rem;
  min-block-size: 5rem;
  object-fit: contain;
  background: var(--cds-layer-accent);
  padding: var(--cds-spacing-03);
}

.cds-logo__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 12rem;
  block-size: 5rem;
  border: 1px dashed var(--cds-border-strong);
  color: var(--cds-text-helper);
  font-size: var(--cds-type-02);
}

.cds-logo__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--cds-spacing-03);
}

.cds-logo__input { font-size: var(--cds-type-03); color: var(--cds-text-secondary); }

/* ============================================================================
   QUICK ACTIONS (SYSTEM-REVIEW-001 R-06)

   A wrapping row of equal tiles rather than a grid with a fixed column count:
   how many appear depends on what the signed-in person may do, and a cashier
   who sees one action should get one tile, not one tile and five gaps.
   ========================================================================= */

.cds-quick {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cds-spacing-04);
  margin-block-start: var(--cds-spacing-04);
}

.cds-quick__action {
  display: flex;
  align-items: center;
  gap: var(--cds-spacing-04);
  min-inline-size: 12rem;
  flex: 1 1 12rem;
  padding: var(--cds-spacing-05);
  border: 1px solid var(--cds-border-subtle);
  background: var(--cds-layer);
  color: var(--cds-text-primary);
  text-decoration: none;
}

.cds-quick__action:hover {
  background: var(--cds-layer-hover);
  border-color: var(--cds-border-strong);
}

.cds-quick__action:focus-visible {
  outline: 2px solid var(--cds-focus);
  outline-offset: -2px;
}

/* A fixed box so the labels line up down the column however wide the glyph is.
   A character, not an icon: the word beside it already says what this does. */
.cds-quick__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 2rem;
  block-size: 2rem;
  flex: 0 0 auto;
  background: var(--cds-layer-accent);
  color: var(--cds-interactive);
  font-size: var(--cds-type-05);
}

.cds-quick__label { font-size: var(--cds-type-04); }

/* The count of what a reset destroys. Two columns, figures aligned to the inline
   end, because the numbers are the argument and they should be scannable. */
.cds-reset__counts {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--cds-spacing-02) var(--cds-spacing-06);
  margin: var(--cds-spacing-04) 0 var(--cds-spacing-06);
  max-inline-size: 30rem;
  font-size: var(--cds-type-03);
}

.cds-reset__counts dt { color: var(--cds-text-secondary); }
.cds-reset__counts dd { margin: 0; text-align: end; color: var(--cds-text-primary); }
