/**
 * @kodo/ui — self-hosted brand faces.
 *
 * Same two families, same variable woff2 files, same font-display as the marketing
 * site (kodo.lk), so type is identical across marketing → app → storefront.
 *
 * Variable fonts: one file covers the whole weight axis, which is why `font-weight`
 * is a RANGE. Do not split these into per-weight @font-face rules — that would defeat
 * the variable axis and force extra downloads.
 *
 * Paths are relative on purpose:
 *   • dashboard (Vite) imports this file, so Vite resolves + hashes the woff2.
 *   • the SSR apps read this file at boot and rewrite "/assets/fonts/" → "/assets/fonts/",
 *     which they serve themselves. See apps/storefront/src/index.ts.
 * Keep the "/assets/fonts/" prefix intact or that rewrite silently stops matching.
 *
 * Sinhala/Tamil are NOT covered by these faces — Noto handles those, see --font-sans
 * in tokens.css.
 */

@font-face {
  font-family: "Gabarito";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("/assets/fonts/gabarito-variable.woff2") format("woff2");
}

@font-face {
  font-family: "Hanken Grotesk";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/fonts/hanken-grotesk-variable.woff2") format("woff2");
}

@font-face {
  font-family: "Hanken Grotesk";
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/fonts/hanken-grotesk-italic-variable.woff2") format("woff2");
}

/**
 * @kodo/ui — design tokens
 *
 * Three layers:
 *   1. Primitives  — raw ramps (neutrals + brand palette --p-*). Never used directly by components.
 *   2. Semantic    — --bg, --text, --primary… Components ONLY consume these.
 *   3. Overrides   — [data-theme="dark"] flips semantics; [data-palette] swaps the brand ramp;
 *                    per-store branding (stores.branding jsonb) injects --p-* inline on storefronts.
 *
 * SOURCE OF TRUTH: the marketing site, kodo.lk. Every brand value below is copied
 * verbatim from its :root block, so marketing → dashboard → storefront are one system.
 * Do not "improve" a hex here — change it on the marketing site first, then mirror it.
 * (The previous Mango-orange ramp is gone; so is the pink/violet family that had crept
 * into brand-auth.css and never existed on kodo.lk.)
 *
 *   cream #f5ebe2 · coral #f55e4a · citron #e8f23f · blush #ffe2dd · green #276e67
 *   panel #faf5ef · card #fbf8f3 · line #e4d9ce · ink #16130f · body #473e38
 *
 * Personality: warm & friendly. Cream neutrals, generous radii, soft shadows.
 * Type: Gabarito (display) + Hanken Grotesk (body), self-hosted — see fonts.css.
 * Latin comes from those two; Sinhala + Tamil fall through to Noto in --font-sans,
 * because product names are not English-only and neither brand face covers those scripts.
 */

:root {
  /* ---------- primitives: warm neutrals (cream → ink) ----------
     Anchored on the marketing site's neutrals; the 400/850/950 steps are
     interpolated to keep the ramp evenly spaced for the dark theme. */
  --n-0: #ffffff;    /* white  */
  --n-50: #fbf8f3;   /* card   */
  --n-100: #faf5ef;  /* panel  */
  --n-200: #f5ebe2;  /* cream  */
  --n-300: #e4d9ce;  /* line   */
  --n-400: #c9bcae;  /* (interpolated) */
  --n-500: #8a7f76;  /* muted-2 */
  --n-600: #6b615a;  /* muted  */
  --n-700: #473e38;  /* body   */
  --n-800: #3b342e;  /* ink-2  */
  --n-850: #241f1a;  /* (interpolated) */
  --n-900: #16130f;  /* ink    */
  --n-950: #0d0b09;  /* (interpolated) */

  /* ---------- primitives: brand ramp — Coral ----------
     600 is coral and 700 is coral-dark, both verbatim from kodo.lk, because
     --primary maps to 600 and --primary-hover to 700. The remaining stops are
     tints/shades of coral; 100 is the marketing "blush".
     Per-store branding overwrites this whole ramp inline — see paletteOverrideCss. */
  --p-50: #fff4f2;
  --p-100: #ffe2dd;  /* blush */
  --p-200: #ffc9c1;
  --p-300: #fdaa9e;
  --p-400: #fa8a79;
  --p-500: #f76f5a;
  --p-600: #f55e4a;  /* coral */
  --p-700: #d8402c;  /* coral-dark */
  --p-800: #b0321f;
  --p-900: #8a2717;

  /* ---------- primitives: brand extras ----------
     Citron and green are first-class marketing colours with no slot in the
     --p-* ramp (they are not tints of coral). Exposed so components have an
     on-brand accent to reach for instead of inventing a new hex. */
  --coral: #f55e4a;        /* alias of --p-600; the three named brand hues */
  --citron: #e8f23f;
  --citron-ink: #16130f;   /* citron is near-yellow: only ink is readable on it */
  --citron-deep: #6f7a12;  /* darkened citron, for a glyph that must sit on white */
  --casal: #276e67;
  --green: var(--casal);   /* legacy name, kept so existing rules keep resolving */
  --casal-deep: #225a54;
  --green-deep: var(--casal-deep);
  --on-casal: #e2eeea;
  --on-green: var(--on-casal);
  --on-green-dim: #d2e4df;
  --cream: #f5ebe2;
  --blush: #ffe2dd;
  /* Citron washed out for "coming soon": the pill, its ink and its dot. Citron
     itself is too loud for a label that says "not yet". */
  --citron-soft: #f3f6bf;
  --citron-soft-ink: #55600c;
  /* Third-party brand colour, fixed in every palette and theme: WhatsApp's own
     green, for anything that stands for a WhatsApp button (the editor's colour
     swatch for it). Never a fill for our own UI. */
  --whatsapp-green: #25d366;
  --citron-mid: #9aa514;

  /* ---------- primitives: fixed status hues ----------
     DELIBERATE EXCEPTION to "marketing colours only". kodo.lk has no amber, red
     or blue, but order state has to be readable at a glance: "cancelled" must not
     look like "delivered". These are warmed to sit next to cream without going
     muddy, and are palette-independent so per-store branding cannot break them.
     Not decoration — do not reuse them for anything but state. */
  --green-bg: #dcefe4;   --green-text: #14513a;  --green-solid: #1e7a52;
  --amber-bg: #fbeecb;   --amber-text: #7c4510;  --amber-solid: #c9760a;
  --red-bg: #fde4de;     --red-text: #8f2417;    --red-solid: #cf3b23;
  --blue-bg: #dde8f3;    --blue-text: #1c3f66;   --blue-solid: #2b6cab;

  /* ---------- semantic: surfaces ---------- */
  --bg: var(--n-100);
  --surface: var(--n-0);
  --surface-sunken: var(--n-200);
  --surface-hover: var(--n-200);
  --border: var(--n-300);
  --border-strong: var(--n-400);
  --overlay: rgb(22 19 15 / 0.45);
  /* Every dialog's backdrop blurs the page behind it as well as veiling it
     (modals, drawers, the bulk panel, search, the phone menu), in both themes.
     Photo viewers keep their own dark, unblurred veil: the photo is the point. */
  --overlay-blur: 6px;

  /* Idle fill for form fields (input/select/textarea) — deliberately a flat
     neutral grey rather than --surface-sunken's warm cream, which read as a
     faded dark orange against a white card. Fields carry no border at rest;
     the coral border + glow is focus-only (see .k-input:focus-visible). */
  --field-bg: #e7e7e7;

  /* A warm panel INSIDE a white card — an option row, a variant card, a table
     head. Cream in light; in dark one step up from the card, never cream (a
     cream slab with light text on it is the bug this token exists to stop). */
  --surface-warm: var(--cream);

  /* SECONDARY (_READ-THESE/COLOURS.md): the frame colour as a quiet UI accent
     rather than a fill: a soft tinted background and the text/icon that sits
     on it, e.g. the Settings tab strip. Casal itself is too dark to read as
     text on a dark surface, hence a token with two values. A palette points
     both at its own frame (palettes.ts). */
  --secondary-soft: color-mix(in srgb, var(--casal) 12%, transparent);
  --secondary-text: var(--casal);

  /* FEATURE: the loud cards (the playbook strip on Home, the Tensho card on
     Website). The frame colour as the ground, white text, and the HIGHLIGHT
     (kodo citron here) for the tag and the button. A palette sets all five
     (palettes.ts featureColours); nothing else should paint casal or citron
     on a card directly, or the card won't follow the palette. */
  --feature: var(--casal);
  --feature-deep: var(--casal-deep);
  --on-feature: var(--n-0);
  --highlight: var(--citron);
  --on-highlight: var(--citron-ink);

  /* SPOTLIGHT: the pinned update on Home. The palette picks it, not the
     update: Kodo's is the Feature card (casal, citron tag); a palette whose
     frame is a near-black takes its primary instead (Kodo Ink: coral), so
     the pinned card is never a dark slab. palettes.ts spotlightColours. */
  --spotlight: var(--feature);
  --on-spotlight: var(--on-feature);
  --spotlight-tag: var(--highlight);
  --on-spotlight-tag: var(--on-highlight);
  --spotlight-accent: var(--highlight);

  /* Shadow COLOUR for one-off box-shadows that are not one of --shadow-*: ink
     on light, black on dark. Use as color-mix(in srgb, var(--shadow-ink) 20%, transparent). */
  --shadow-ink: var(--n-900);

  /* ---------- semantic: text ---------- */
  --text: var(--n-700);
  --text-muted: var(--n-600);
  --text-faint: var(--n-500);
  --text-invert: var(--n-0);
  --text-strong: var(--n-900);   /* headings — marketing uses full ink, not body */
  /* InfoTip's own colours. Declared here, on the root, so they resolve ONCE:
     a parent that repaints --text or --surface (a selected coral card) can't
     turn the bubble's text white-on-white or hide its icon. */
  --infotip-bg: var(--n-0);
  --infotip-text: var(--n-700);
  --infotip-title: var(--n-900);
  /* Light grey bubble, darker grey "i"; one shade deeper on hover/open. */
  --infotip-icon: #e7e7e7;
  --infotip-icon-mark: var(--n-600);
  --infotip-icon-on: #d6d6d6;
  --infotip-icon-mark-on: var(--n-800);
  --infotip-ring: var(--n-0);
  /* Floating bubbles (InfoTip, Tooltip, the sidebar's coming-soon explainer):
     filter, not box-shadow, so a bubble's tail is shadowed with its body. */
  --bubble-filter: drop-shadow(0 6px 16px rgb(22 19 15 / 0.14)) drop-shadow(0 1px 3px rgb(22 19 15 / 0.08));
  --popover-bg: var(--n-0);

  /* ---------- semantic: brand ---------- */
  --primary: var(--p-600);
  --primary-hover: var(--p-700);
  --primary-active: var(--p-800);
  --primary-soft: var(--p-100);
  --primary-soft-text: var(--p-800);
  --on-primary: #ffffff;
  --focus-ring: var(--p-400);
  --link: var(--p-700);

  /* ---------- semantic: dashboard shell ----------
     The dark frame (the rail plus the strip round the page) and the canvas each
     page sits on. Only the dashboard's shell reads these; the storefront never
     does, so they can move without touching a single shop.

     Brand colours only. The rail is brand casal, the rail's colour before
     KD-35 and again now: the darker experiments (ink, warm charcoal, "casal
     night") were all tried and dropped. Casal is a mid-tone, so the step to
     the cream canvas is a gentle ~5:1 rather than the ~17:1 glare ink had.
     Hover is --casal-deep; the active item is the original citron wash with
     citron text. Labels are --on-casal (~5:1). */
  --canvas: var(--cream);            /* the page panel, the same on every route; white cards lift off it */
  --rail: var(--casal);
  --rail-sunken: var(--casal-deep);  /* store card, search, App store card, foot tiles */
  --rail-hover: var(--casal-deep);
  --rail-active: color-mix(in srgb, var(--citron) 16%, transparent);        /* the citron wash */
  --rail-active-strong: color-mix(in srgb, var(--citron) 24%, transparent); /* active inside a flyout, which sits on --rail-hover */
  --rail-active-text: var(--citron); /* the active item's label, as the casal rail always had it */
  /* Rail text is white-based: white is the most casal can carry (6.0:1), and
     the old on-casal labels (5.0:1) and their mixes read as too soft. */
  --rail-text: var(--n-0);           /* names, headings — 6.0:1 */
  --rail-label: var(--n-0);          /* item labels — 6.0:1 */
  --rail-subtle: color-mix(in srgb, var(--n-0) 95%, var(--rail)); /* sub-items — 5.6:1 */
  --rail-faint: color-mix(in srgb, var(--n-0) 85%, var(--rail));  /* icons, hints, coming-soon labels — 4.8:1 */
  --rail-dim: color-mix(in srgb, var(--n-0) 75%, var(--rail));    /* coming-soon icons — 4.2:1 */
  --rail-line: color-mix(in srgb, var(--on-casal) 16%, transparent);
  --rail-line-strong: color-mix(in srgb, var(--on-casal) 28%, transparent);
  --rail-accent: var(--citron);      /* active icon, focus ring */

  /* ---------- semantic: status ---------- */
  --success-bg: var(--green-bg);   --success-text: var(--green-text);   --success: var(--green-solid);
  --warning-bg: var(--amber-bg);   --warning-text: var(--amber-text);   --warning: var(--amber-solid);
  --danger-bg: var(--red-bg);      --danger-text: var(--red-text);      --danger: var(--red-solid);
  --info-bg: var(--blue-bg);       --info-text: var(--blue-text);       --info: var(--blue-solid);

  /* ---------- type ----------
     Stacks mirror kodo.lk's --f-display / --f-body, with Noto appended so
     Sinhala and Tamil still render (Gabarito/Hanken cover Latin only). */
  --font-display: "Gabarito", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-sans: "Hanken Grotesk", "Noto Sans Sinhala", "Noto Sans Tamil",
    system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --text-xs: 0.75rem;    /* 12 — meta, table captions */
  --text-sm: 0.8125rem;  /* 13 — secondary UI */
  --text-md: 0.875rem;   /* 14 — body / default UI */
  --text-lg: 1rem;       /* 16 — emphasized body */
  --text-xl: 1.1875rem;  /* 19 — card titles */
  --text-2xl: 1.5rem;    /* 24 — page titles */
  --text-3xl: 1.9375rem; /* 31 — hero numbers */

  --leading-tight: 1.25;
  --leading-normal: 1.55;

  --weight-normal: 400;
  --weight-medium: 500;
  /* Was referenced in 24 places across components.css and the page stylesheets
     but never declared, so every one of those font-weight declarations was
     invalid and silently inherited. Defining it is what makes those weights
     actually apply. */
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-display: 800;  /* Gabarito is a variable face: 800 is the marketing display weight */

  /* ---------- space (4px base) ---------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;

  /* ---------- layout: large-display scaling ----------
     --shell caps content width on 2K/4K instead of letting lines run to 3800px.
     NOTE: kodo.lk uses clamp(1450px, 95vw, 1760px); that floor is wider than a
     1366px laptop viewport and would force horizontal scroll here, so the floor
     is 0 and the cap is kept. Same ceiling, no overflow. */
  --shell: min(1760px, 95vw);
  --shell-narrow: min(1200px, 95vw);  /* reading-width views: settings, forms */
  --gut: clamp(16px, 2.6vw, 32px);

  /* ---------- radius (friendly-rounded, per kodo.lk) ---------- */
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-xl: 32px;
  --radius-full: 999px;

  /* ---------- elevation (shadow tint = ink, not black) ---------- */
  --shadow-sm: 0 3px 10px rgb(22 19 15 / 0.10);
  --shadow-md: 0 2px 8px rgb(22 19 15 / 0.08), 0 1px 2px rgb(22 19 15 / 0.04);
  --shadow-lg: 0 8px 24px rgb(22 19 15 / 0.12), 0 2px 6px rgb(22 19 15 / 0.06);
  /* A borderless surface lifted just off the page (cards, toolbars): the
     shadow is the edge, so it stays close and soft rather than floating. */
  --shadow-soft: 0 1px 2px rgb(22 19 15 / 0.06), 0 3px 10px rgb(22 19 15 / 0.07);

  /* ---------- motion ---------- */
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --duration-fast: 120ms;
  --duration-normal: 200ms;
  /* Quick disclosures and overlays. Keep related surfaces in sync.
     Reference: https://polaris-react.shopify.com/tokens/motion */
  --duration-enter: 150ms;
  --duration-exit: 100ms;
  --duration-disclosure: 150ms;
  /* Revealing content in place: an accordion sliding open. A symmetric curve
     (slow start, slow finish) reads as smooth where --ease's quick start reads
     as a pop. */
  --duration-reveal: 300ms;
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);

  color-scheme: light;
}

/* ============================================================
   Fluid root type — the 2K/4K story.
   Copied from kodo.lk: 16px up to ~1530px viewport, ramping to 20px by ~2740px.
   Every --text- and --space- token is in rem, so the whole UI scales with it and a
   4K display shows a proportionally larger app rather than the same UI marooned
   in the middle of the screen. Fixed-px rules do NOT scale — those are being
   migrated onto tokens; until then expect minor proportion drift above 2K.
   ============================================================ */
html {
  font-size: clamp(16px, 0.33vw + 10.96px, 20px);
}

/* ============================================================
   Dark theme — flips semantic layer only.
   Default stays light; opt in with <html data-theme="dark">.
   ============================================================ */
[data-theme="dark"] {
  /* Cool neutral darks. The light ramp's dark end is warm ink (cream → ink),
     and dark mode used it as is: brown-black pages and cards that clashed with
     the neutral grey rail. Re-pointed here, inside dark only, because light
     mode still uses these steps as ink text. Each step keeps its role. */
  --n-400: #c3c6cc;   /* muted text */
  --n-500: #8b9098;   /* faint text: 5.1:1 on a card (the warm one was 4.2) */
  --n-700: #4a4d54;
  --n-800: #2b2d32;   /* hover */
  --n-850: #1e1f23;   /* cards */
  --n-900: #141518;   /* the page, a step lighter than the rail */
  --n-950: #0b0c0e;

  --bg: var(--n-950);
  --surface: var(--n-850);
  --surface-sunken: var(--n-900);
  --surface-hover: var(--n-800);
  --border: #2c2e33;
  --border-strong: #3d4047;
  /* Dark mode lifts the page behind a dialog with a light veil instead of
     sinking it further into black: the dialog's own dark surface then stands
     clear of it. Tried on Products, adopted sitewide (28 Oct 2026). */
  --overlay: color-mix(in srgb, var(--text-strong) 16%, transparent);

  --text: #f3f4f6;
  --text-muted: var(--n-400);
  --text-faint: var(--n-500);
  --text-invert: var(--n-900);
  --text-strong: #fafbfc;
  /* Lifted one step above a card (--n-850): at the card's own colour a tip
     melted into whatever it opened over. A hairline edge and a real black
     shadow carry the outline the light theme gets from its soft ink shadow. */
  --infotip-bg: color-mix(in srgb, var(--n-0) 9%, var(--n-850));
  --popover-bg: color-mix(in srgb, var(--n-0) 9%, var(--n-850));
  --bubble-filter: drop-shadow(0 0 0.5px rgb(255 255 255 / 0.32)) drop-shadow(0 10px 22px rgb(0 0 0 / 0.55)) drop-shadow(0 2px 4px rgb(0 0 0 / 0.4));
  --infotip-text: #f3f4f6;
  --infotip-title: #fafbfc;
  --infotip-icon: var(--n-800);
  --infotip-icon-mark: var(--n-400);
  --infotip-icon-on: var(--n-700);
  --infotip-icon-mark-on: var(--n-200);
  --infotip-ring: var(--n-850);

  --primary: var(--p-500);
  --primary-hover: var(--p-400);
  --primary-active: var(--p-300);
  --primary-soft: color-mix(in srgb, var(--p-500) 18%, transparent);
  --primary-soft-text: var(--p-300);
  --focus-ring: var(--p-400);
  --link: var(--p-400);

  /* The canvas goes to brand ink. The rail follows it into the dark (below). */
  --canvas: var(--n-900);

  /* Fields sit one step above the card they are on, so they still read as
     wells. The light value (#e7e7e7) under light text was the "grey dropdown
     with white text" bug. */
  --field-bg: color-mix(in srgb, var(--n-0) 7%, var(--n-850));
  --surface-warm: color-mix(in srgb, var(--n-0) 4%, var(--n-850));
  --secondary-soft: color-mix(in srgb, var(--casal) 30%, transparent);
  --secondary-text: color-mix(in srgb, var(--casal) 45%, var(--n-0));
  --shadow-ink: #000;

  --success-bg: color-mix(in srgb, var(--green-solid) 18%, transparent);
  --success-text: #6ee7a8;
  --warning-bg: color-mix(in srgb, var(--amber-solid) 18%, transparent);
  --warning-text: #fbbf24;
  --danger-bg: color-mix(in srgb, var(--red-solid) 18%, transparent);
  --danger-text: #f8917d;
  --info-bg: color-mix(in srgb, var(--blue-solid) 18%, transparent);
  --info-text: #93c5fd;

  --shadow-sm: 0 3px 10px rgb(0 0 0 / 0.5);
  --shadow-md: 0 2px 8px rgb(0 0 0 / 0.5);
  --shadow-lg: 0 8px 24px rgb(0 0 0 / 0.6);
  --shadow-soft: 0 1px 2px rgb(0 0 0 / 0.4), 0 3px 10px rgb(0 0 0 / 0.35);

  color-scheme: dark;
}

/* Dark, on the default palette: the rail is a neutral near-black grey, darker
   than the page, the way dark apps do it, and casal moves into the type. A
   casal slab beside a black page read as out of place; so did a casal-tinted
   black. Labels are white and the icons casal, lifted toward white (plain
   casal is only 3.2:1 on black); the current link is a solid casal pill with white text and the
   citron icon. No hairline round the page panel: the step from the rail to
   the page is enough.
   :not([data-palette]): a dashboard palette (palettes.ts) sets its own rail in
   both themes, and lib/palette-pref.ts marks <html> with data-palette while one
   is applied, so this never repaints it. */
:root[data-theme="dark"]:not([data-palette]) {
  --rail: #0e0e0f;                   /* neutral, a step darker than the page (--n-900) */
  --rail-sunken: #1b1b1c;            /* store card, search, App store card, foot tiles */
  --rail-hover: #1b1b1c;
  --rail-active: var(--casal);       /* the current link: solid casal */
  --rail-active-strong: var(--casal);
  --rail-active-text: var(--n-0);    /* white on casal, 6.0:1; the icon stays citron (4.9:1) */
  --rail-active-sub-text: var(--n-0); /* a current sub-link, on the same pill */
  /* Type is white, a touch off pure so it doesn't glare on near-black; the
     icons carry the casal. */
  --rail-text: color-mix(in srgb, var(--n-0) 94%, var(--rail));    /* names: 16:1 */
  --rail-label: color-mix(in srgb, var(--n-0) 90%, var(--rail));   /* item labels: 15:1 */
  --rail-subtle: color-mix(in srgb, var(--n-0) 80%, var(--rail));  /* sub-items: 11:1 */
  --rail-faint: color-mix(in srgb, var(--casal) 75%, var(--n-0));  /* icons, coming-soon labels: 5.5:1 */
  --rail-dim: color-mix(in srgb, var(--casal) 85%, var(--n-0));    /* coming-soon icons: 4.5:1 */
  --rail-line: color-mix(in srgb, var(--n-0) 8%, transparent);
  --rail-line-strong: color-mix(in srgb, var(--n-0) 14%, transparent);
}

/* ============================================================
   Per-store branding: inject --p-50…900 inline on <html> to
   re-skin a storefront. The semantic layer re-derives automatically.
   Brand extras (--citron/--green) and the status hues are intentionally
   NOT overridable — a seller re-skins their shop, not kodo's state colours.
   See DESIGN.md §per-store-branding.
   ============================================================ */

/* ============================================================
   Base reset (minimal, opinionated)
   ============================================================ */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The UA rule for [hidden] is display:none at the element level, so ANY class
   that sets display silently defeats it. Restoring it here means hidden={...}
   behaves the way every developer expects. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background var(--duration-normal) var(--ease);
}

/* Display face on headings — matches kodo.lk, where every heading is Gabarito. */
h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  color: var(--text-strong);
  letter-spacing: -0.02em;
  line-height: var(--leading-tight);
}

p {
  margin: 0;
}

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

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Never let a long word or a wide table push the page sideways — the single most
   common mobile defect. Wide children opt into their own scroll container. */
img, svg, video, canvas {
  max-width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/**
 * @kodo/ui — component classes
 *
 * Plain CSS on top of tokens.css. Works identically in the React dashboard
 * and the SSR storefront (template strings) — no build step required.
 * Prefix: .k-
 */

/* ============================================================ buttons */
.k-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  font: inherit;
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  line-height: 1;
  padding: 0 var(--space-4);
  height: 2.5rem;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--duration-fast) var(--ease),
    border-color var(--duration-fast) var(--ease),
    color var(--duration-fast) var(--ease);
}
.k-btn:disabled,
.k-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.k-btn--primary {
  background: var(--primary);
  color: var(--on-primary);
}
.k-btn--primary:hover:not(:disabled) { background: var(--primary-hover); }
.k-btn--primary:active:not(:disabled) { background: var(--primary-active); }

/* Ghost outline in the primary colour (coral, unless a dashboard palette
   changes the accent) — same family as primary, quieter because it has no
   fill until you interact with it. Replaces a plain grey outline that read as
   a neutral, unrelated button next to a coral primary. */
.k-btn--secondary {
  background: transparent;
  border-color: var(--primary);
  color: var(--primary);
}
.k-btn--secondary:hover:not(:disabled) { background: var(--primary); color: var(--on-primary); }

.k-btn--soft {
  background: var(--primary-soft);
  color: var(--primary-soft-text);
}
.k-btn--soft:hover:not(:disabled) {
  background: color-mix(in srgb, var(--primary-soft) 80%, var(--primary));
}

.k-btn--ghost {
  background: transparent;
  color: var(--text-muted);
}
.k-btn--ghost:hover:not(:disabled) {
  background: var(--surface-hover);
  color: var(--text);
}

.k-btn--danger {
  background: var(--danger);
  color: #fff;
}
.k-btn--danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--danger) 85%, black);
}

.k-btn--sm { height: 2rem; padding: 0 var(--space-3); font-size: var(--text-sm); border-radius: var(--radius-full); }
.k-btn--lg { height: 3rem; padding: 0 var(--space-6); font-size: var(--text-lg); border-radius: var(--radius-full); }
.k-btn--block { width: 100%; }

/* ============================================================ form controls */
.k-input,
.k-select,
.k-textarea {
  display: block;
  width: 100%;
  background: var(--field-bg);
  /* Transparent, not none — keeps the 1px box so focus doesn't shift layout
     when :focus-visible below swaps the colour in. */
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  color: var(--text);
  font: inherit;
  font-size: var(--text-md);
  padding: 0 var(--space-3);
  height: 2.5rem;
  transition: border-color var(--duration-fast) var(--ease);
}

/* .k-select is now a themed listbox trigger (a <button>, not a native
   <select> — see forms.tsx), so its layout diverges from the shared block
   above: flex row for the value + chevron, extra right padding so the
   chevron doesn't sit flush against the edge (the native arrow it replaces
   used to). Declared after the shared rule so it wins on `display`. */
.k-select-wrap { position: relative; display: block; width: 100%; }
.k-select {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  text-align: left;
  cursor: pointer;
  padding: 0 var(--space-3);
}
.k-select__value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-select__chevron { flex: 0 0 auto; color: var(--text-muted); transition: transform var(--duration-fast) var(--ease); }
.k-select[aria-expanded="true"] .k-select__chevron { transform: rotate(180deg); }

.k-select__panel {
  /* Fixed, and placed by Select from the trigger's box (forms.tsx), so a
     scrolling ancestor's overflow can't clip it. */
  position: fixed;
  z-index: 60;
  top: 0;
  left: 0;
  width: max-content;
  max-width: min(28rem, 90vw);
  /* Tall enough for the lists we ship without a scrollbar, still capped so a
     genuinely long one doesn't run off the screen. Select lowers it further
     when there's less room, and opens upward when there's more room above. */
  max-height: 20rem;
  overflow-y: auto;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  /* A floating panel: lifted by its shadow, no border (STYLE-GUIDE.md). The
     popover fill is a step lighter than a card in dark, so it still reads there. */
  background: var(--popover-bg);
  box-shadow: var(--shadow-lg);
}
.k-select__option {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-md);
  cursor: pointer;
}
.k-select__option--highlight { background: var(--primary-soft); }
.k-select__option--selected { color: var(--primary); font-weight: var(--weight-semibold); }
.k-select__option--disabled { color: var(--text-faint); cursor: not-allowed; }
.k-textarea {
  /* Full pill roundness looks wrong on a multi-line box — kept rectangular,
     with the same corner radius the platform used before "fully rounded"
     meant fully rounded. */
  border-radius: var(--radius-md);
  height: auto;
  min-height: 5.5rem;
  padding: var(--space-2) var(--space-3);
  line-height: var(--leading-normal);
  resize: vertical;
}
.k-input::placeholder,
.k-textarea::placeholder { color: var(--text-faint); }
.k-input:focus-visible,
.k-select:focus-visible,
.k-textarea:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 0;
  border-color: var(--primary);
}
.k-input[aria-invalid="true"],
.k-select[aria-invalid="true"],
.k-textarea[aria-invalid="true"] { border-color: var(--danger); }
.k-input:disabled,
.k-select:disabled,
.k-textarea:disabled {
  background: var(--surface-sunken);
  color: var(--text-faint);
  cursor: not-allowed;
}

/* input with a fixed prefix and/or suffix (e.g. slug fields: "kodo.lk/" - ".kodoshop.lk") */
.k-input-group {
  display: flex;
  align-items: stretch;
}
.k-input-group__affix {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 0 var(--space-3);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  font-size: var(--text-sm);
  white-space: nowrap;
}
.k-input-group__affix:first-child {
  border-right: 0;
  border-radius: var(--radius-full) 0 0 var(--radius-full);
}
.k-input-group__affix--end {
  border-left: 0;
  border-radius: 0 var(--radius-full) var(--radius-full) 0;
}
/* the input shrinks inside the group instead of pushing an affix out of the card */
.k-input-group .k-input {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}
.k-input-group .k-input:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.k-input-group .k-input:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }

.k-checkbox {
  width: 1.125rem;
  height: 1.125rem;
  accent-color: var(--primary);
  cursor: pointer;
}

/* switch */
.k-switch {
  appearance: none;
  width: 2.5rem;
  height: 1.5rem;
  border-radius: var(--radius-full);
  background: var(--border-strong);
  position: relative;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease);
  flex-shrink: 0;
}
.k-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: var(--radius-full);
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition: translate var(--duration-fast) var(--ease);
}
.k-switch:checked { background: var(--primary); }
.k-switch:checked::after { translate: 1rem 0; }

/* field wrapper */
.k-field { display: flex; flex-direction: column; gap: var(--space-2); }
.k-field__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text);
}
.k-field__hint { font-size: var(--text-sm); color: var(--text-muted); }
.k-field__error { font-size: var(--text-sm); color: var(--danger-text); }
.k-field__label-row { display: inline-flex; align-items: center; gap: 0.375rem; }
.k-switch-row__label .k-infotip,
.k-read-field__label .k-infotip { margin-inline-start: 0.375rem; }

/* ============================================================ infotip
   The "?" speech bubble beside a label, and the comic-style bubble it opens:
   borderless, a soft shadow, and a slanted tail pointing back at the icon.
   `filter: drop-shadow` (not box-shadow) so the tail is shadowed with the box. */
.k-infotip { display: inline-flex; vertical-align: middle; line-height: 0; }
/* Tips switched off by the seller (see InfoTip.tsx, tipsOff). */
:root[data-tooltips="off"] .k-infotip { display: none; }
/* Tooltip (no icon): wraps a control in place; the bubble is InfoTip's. */
.k-tooltip { display: inline-flex; }
.k-infotip__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  margin: -0.25rem;            /* a bigger tap target without moving the label */
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.k-infotip__btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* The ring keeps the bubble visible on a coral background (a selected card). */
.k-infotip__shape { fill: var(--infotip-icon); stroke: var(--infotip-ring); stroke-width: 1; paint-order: stroke; transition: fill 0.15s ease; }
.k-infotip__mark { fill: none; stroke: var(--infotip-icon-mark); stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; transition: stroke 0.15s ease; }
.k-infotip__dot { fill: var(--infotip-icon-mark); transition: fill 0.15s ease; }
.k-infotip__btn:hover .k-infotip__shape,
.k-infotip__btn[aria-expanded="true"] .k-infotip__shape { fill: var(--infotip-icon-on); }
.k-infotip__btn:hover .k-infotip__mark,
.k-infotip__btn[aria-expanded="true"] .k-infotip__mark { stroke: var(--infotip-icon-mark-on); }
.k-infotip__btn:hover .k-infotip__dot,
.k-infotip__btn[aria-expanded="true"] .k-infotip__dot { fill: var(--infotip-icon-mark-on); }
.k-infotip__btn[aria-expanded="true"] .k-infotip__icon { animation: k-infotip-wiggle 0.35s ease; }
@keyframes k-infotip-wiggle {
  0% { rotate: 0deg; } 30% { rotate: -12deg; } 65% { rotate: 8deg; } 100% { rotate: 0deg; }
}

.k-infotip__bubble {
  position: fixed;
  z-index: 2100;               /* above drawers and the upgrade tooltip */
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  width: max-content;
  max-width: min(17rem, calc(100vw - 24px)); /* 2 × BUBBLE_EDGE (placeBubble.ts) */
  padding: 0.625rem 0.8rem;
  border-radius: var(--radius-md);
  background: var(--infotip-bg);
  filter: var(--bubble-filter);
  /* It is a [popover] (top layer, see lift() in InfoTip.tsx): undo the UA's
     centring inset/margin, border and overflow — overflow would clip the tail. */
  margin: 0;
  inset: auto;
  border: 0;
  overflow: visible;
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  font-weight: var(--weight-normal);
  line-height: 1.5;
  letter-spacing: normal;
  text-transform: none;
  text-align: left;
  white-space: normal;
  color: var(--infotip-text);
  cursor: auto;
  animation: k-infotip-pop 0.14s ease-out;
}
/* The tail: a slanted wedge, comic-style, anchored at --tail-x (set in JS). */
.k-infotip__bubble::after {
  content: "";
  position: absolute;
  left: calc(var(--tail-x, 50%) - 7px);
  width: 14px;
  height: 9px;
  background: var(--infotip-bg);
}
.k-infotip__bubble--top::after { top: calc(100% - 1px); clip-path: polygon(0 0, 100% 0, 25% 100%); }
.k-infotip__bubble--bottom::after { bottom: calc(100% - 1px); clip-path: polygon(25% 0, 100% 100%, 0 100%); }
.k-infotip__title { font-family: var(--font-display); font-size: 0.875rem; font-weight: 700; color: var(--infotip-title); }
.k-infotip__text { color: var(--infotip-text); }
.k-infotip__text a { color: var(--primary-hover); }
@keyframes k-infotip-pop { from { opacity: 0; transform: translateY(2px) scale(0.97); } }
@media (prefers-reduced-motion: reduce) {
  .k-infotip__bubble,
  .k-infotip__btn[aria-expanded="true"] .k-infotip__icon { animation: none; }
}

/* ============================================================ card */
.k-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.k-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.k-card__title { font-size: var(--text-lg); font-weight: var(--weight-bold); }
.k-card__body { padding: var(--space-5); }
.k-card__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border);
  background: var(--surface-sunken);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* ============================================================ badge
   Tones map to kodo domain statuses (see STATUS_TONES in @kodo/ui):
   neutral  draft / archived / unpaid
   info     pending_confirmation / slip_submitted / packed / dispatched
   success  confirmed / delivered / verified / connected / active
   warning  cod_collected-pending-ish / degraded states
   danger   cancelled / returned / failed / error / suspended
*/
.k-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  border-radius: var(--radius-full);
  padding: 0.1875rem var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.4;
  white-space: nowrap;
}
.k-badge--neutral { background: var(--surface-sunken); color: var(--text-muted); border: 1px solid var(--border); }
.k-badge--info    { background: var(--info-bg);    color: var(--info-text); }
.k-badge--success { background: var(--success-bg); color: var(--success-text); }
.k-badge--warning { background: var(--warning-bg); color: var(--warning-text); }
.k-badge--danger  { background: var(--danger-bg);  color: var(--danger-text); }
.k-badge--brand   { background: var(--primary-soft); color: var(--primary-soft-text); }

/* ── Status toggle: a badge that IS the switch ──────────────────────────────
   Keeps .k-badge's geometry and the success/neutral tones, so it still reads as
   the status chip it replaced — but it is a real `role="switch"` you click.
   Replaces a separate bordered switch row lower down the product drawer, which
   meant the same value was stated twice on one screen.

   The thumb is absolutely positioned and animated on `left`, not `translate`.
   The pill's width tracks its label, and the two labels differ in length
   ("Active" vs "Inactive"/"Draft") — so the travel distance is only knowable as
   a percentage of the current width. `left` interpolates against that; a
   translate would need a fixed geometry the label will not hold still for. */
.k-status-toggle {
  /* Geometry in two custom properties, so a size variant overrides these and
     every dependent calc below follows. Hardcoding the numbers meant the thumb's
     travel distance and the label's padding could drift out of step. */
  --st-pad: 0.1875rem;
  --st-thumb: 0.875rem;
  --st-gap: var(--space-2);

  position: relative;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  padding: var(--st-pad);
  background: var(--surface-sunken);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.4;
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-normal) var(--ease), color var(--duration-normal) var(--ease), border-color var(--duration-normal) var(--ease);
}
.k-status-toggle__thumb {
  position: absolute;
  top: 50%;
  left: var(--st-pad);
  translate: 0 -50%;
  width: var(--st-thumb);
  height: var(--st-thumb);
  border-radius: 50%;
  background: var(--text-faint);
  transition: left var(--duration-normal) var(--ease), background var(--duration-normal) var(--ease);
}
/* Room for the thumb on whichever side it currently sits. */
.k-status-toggle__label {
  padding: 0 var(--st-gap) 0 calc(var(--st-thumb) + var(--st-pad) * 2);
  transition: padding var(--duration-normal) var(--ease);
}

.k-status-toggle[aria-checked="true"] {
  background: var(--success-bg);
  color: var(--success-text);
  border-color: transparent;
}
.k-status-toggle[aria-checked="true"] .k-status-toggle__thumb {
  left: calc(100% - var(--st-pad) - var(--st-thumb));
  background: var(--success-text);
}
.k-status-toggle[aria-checked="true"] .k-status-toggle__label {
  padding: 0 calc(var(--st-thumb) + var(--st-pad) * 2) 0 var(--st-gap);
}
.k-status-toggle:hover { border-color: var(--border-strong); }
.k-status-toggle[aria-checked="true"]:hover { border-color: var(--success); }
.k-status-toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* Per-variant use, inside the variants table and its mobile cards — the row is
   already dense, so the same control a step down. Three custom properties and
   nothing else: no longhand overrides fighting the base rule's shorthand on
   equal specificity, where the winner would come down to source order. */
.k-status-toggle--sm {
  --st-pad: 0.125rem;
  --st-thumb: 0.6875rem;
  --st-gap: 0.4375rem;
  font-size: 0.6875rem;
}

/* ============================================================ table */
.k-table-wrap {
  overflow-x: auto;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.k-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-md);
}
.k-table th {
  text-align: left;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  background: var(--surface-sunken);
}
.k-table td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.k-table tbody tr:last-child td { border-bottom: 0; }
/* Coral tint, not the generic grey — the same hover the coral-accented
   work-queue/KPI cards use (--primary-soft), for one consistent "hovering
   something coral-branded" feel platform-wide. */
.k-table tbody tr:hover { background: var(--primary-soft); }
.k-table .is-numeric { text-align: right; font-variant-numeric: tabular-nums; }

/* ============================================================ tabs */
/* A strip too wide for its container (long Tamil or Sinhala labels, many tabs,
   a phone) scrolls sideways inside itself instead of running off the page. The
   scrollbar is hidden: the cut-off tab is the cue. The hairline is an inset
   shadow, not a border, so the selected tab's underline can sit on it without
   the scroll box clipping it. */
.k-tabs {
  display: flex;
  gap: var(--space-1);
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  box-shadow: inset 0 -1px 0 var(--border);
}
.k-tabs::-webkit-scrollbar { display: none; }
.k-tab {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  flex: 0 0 auto;
  white-space: nowrap;
  padding: var(--space-2) var(--space-3) var(--space-3);
  font: inherit;
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  cursor: pointer;
}
.k-tab:hover { color: var(--text); }
.k-tab[aria-selected="true"] {
  color: var(--primary);
  border-bottom-color: var(--primary);
}
/* A small count beside a tab's label — e.g. Orders' Incomplete tab (V2-778). */
.k-tab__count {
  display: inline-block; margin-left: var(--space-1); padding: 0 6px;
  min-width: 18px; border-radius: 999px; text-align: center;
  font-size: var(--text-xs); line-height: 18px;
  background: var(--surface-hover); color: var(--text-muted);
}

/* ============================================================ modal (native <dialog>) */
.k-modal {
  border: 0;
  border-radius: var(--radius-xl);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
  padding: 0;
  width: min(28rem, calc(100vw - 2rem));
  /* Fit the viewport, minus a 1rem gutter on each edge, and no more. The old cap
     was min(90vh, 44rem): the 44rem ceiling meant a dialog that WOULD have fitted
     a laptop screen got an inner scrollbar anyway, and vh ignores iOS Safari's
     collapsing toolbars, which put the footer buttons under the browser chrome.
     dvh plus the gutter is the honest measure of "as tall as there is room for".
     The body keeps overflow-y:auto below as a last resort for genuinely long
     copy; header and footer stay pinned either side of it.

     The flex layout is scoped to [open] — an unconditional `display:flex` would
     override the UA `dialog:not([open]) { display:none }` and render the modal
     while closed. */
  max-height: calc(100dvh - 2rem);
  /* Explicit, rather than relying on the UA's centring for modal dialogs. */
  margin: auto;
}
.k-modal[open] { display: flex; flex-direction: column; }
/* Was 34rem, which is narrower than the forms that ask for it. */
.k-modal--lg { width: min(40rem, calc(100vw - 2rem)); }
.k-modal::backdrop { background: var(--overlay); -webkit-backdrop-filter: blur(var(--overlay-blur)); backdrop-filter: blur(var(--overlay-blur)); }
.k-modal__header { padding: var(--space-5) var(--space-5) 0; flex: 0 0 auto; }
.k-modal__title { font-size: var(--text-xl); font-weight: var(--weight-bold); }
.k-modal__body { padding: var(--space-4) var(--space-5); color: var(--text-muted); flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.k-modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5) var(--space-5);
  flex: 0 0 auto;
}

/* ============================================================ spinner */
.k-spinner {
  display: inline-block;
  width: 1.125rem;
  height: 1.125rem;
  border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor;
  border-radius: var(--radius-full);
  animation: k-spin 0.7s linear infinite;
  vertical-align: -0.2em;
}
.k-spinner--lg { width: 2rem; height: 2rem; border-width: 3px; }
@keyframes k-spin { to { rotate: 360deg; } }

/* ============================================================ empty state */
.k-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
  padding: var(--space-12) var(--space-6);
  color: var(--text-muted);
}
.k-empty__icon { font-size: 2rem; line-height: 1; }
.k-empty__title { font-size: var(--text-lg); font-weight: var(--weight-bold); color: var(--text); }

/* ============================================================ toast */
.k-toast {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: var(--n-800);
  color: #f0eeec;
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  box-shadow: var(--shadow-lg);
  font-size: var(--text-md);
}
[data-theme="dark"] .k-toast { background: var(--n-100); color: var(--n-800); }
.k-toast--success { border-left: 3px solid var(--success); }
.k-toast--danger { border-left: 3px solid var(--danger); }

/* ============================================================ app shell + nav (dashboard) */
/* Contain the dashboard at the viewport, including trackpad rubber-banding.
   Auth and onboarding keep their ordinary document scrolling. */
html:has(.k-shell),
body:has(.k-shell) { overflow: hidden; overscroll-behavior: none; }
.k-shell {
  /* Rail width lives in a var so the collapse is a single-property change.
     The handoff spec used position:fixed + matching padding on main, which needs
     two transitions kept in lockstep or you get a gap mid-animation. A grid
     column has no counterpart to desync from. In rem, so it also scales with the
     fluid root on 2K/4K. */
  /* 14rem: the labels are short. The tightest rows, the three foot tiles and
     the App store card's title, are what set this; narrower clips them. */
  --k-rail: 14rem;
  display: grid;
  grid-template-columns: var(--k-rail) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
  /* The dark frame (KD-35): the rail and the strip around the page are one
     surface, and each page sits on it as a rounded panel (.k-main-bg). */
  background: var(--rail);
}
/* Collapsed = icon rail. 3.75rem = 60px at a 16px root: one 2.5rem control
   plus the collapsed rail's padding either side. */
.k-shell--collapsed { --k-rail: 3.75rem; }
/* Animation is gated on .k-shell--anim, which JS adds only after the persisted
   state has been restored — otherwise a seller with a collapsed rail watches it
   slide shut on every single page load. */
.k-shell--anim { transition: grid-template-columns var(--duration-normal) var(--ease); }
.k-sidebar {
  /* The dark rail (KD-35; it was casal green). Re-pointing the SEMANTIC tokens
     here (rather than restyling each child) means every descendant — nav links,
     active pill, user block, sign-out — inverts automatically and keeps working
     if those components change. The values are the shell tokens in tokens.css. */
  --surface: var(--rail);
  --surface-hover: var(--rail-hover);
  --surface-sunken: var(--rail-sunken);
  --text: var(--rail-text);
  --text-muted: var(--rail-label);          /* item labels */
  --text-faint: var(--rail-faint);          /* icons, hints */
  --border: var(--rail-line);
  --border-strong: var(--rail-line-strong);
  --primary-soft: var(--rail-active);       /* the active item's fill */
  --primary-soft-text: var(--rail-active-text);

  background: var(--rail);
  color: var(--text);
  /* The rail's side padding, as a var: the nav list reaches back across it so
     its scrollbar sits on the rail's very edge (nav.css). */
  --k-rail-pad: 0.625rem;
  padding: var(--space-3) var(--k-rail-pad);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  position: sticky;
  top: 0;
  /* The shell owns the viewport; the rail fills its available row. */
  height: 100%;
  overflow-y: auto;
  /* Clip labels as the rail narrows instead of letting them spill past the edge. */
  overflow-x: hidden;
  /* No visible scrollbar. Nav icons cut off at the fold are their own affordance,
     and a bar down the rail was the uglier half of that trade. Still
     scrollable by wheel, trackpad, touch and keyboard. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* min-height:0 lets the nav scroll inside the flex column instead of overflowing */
  min-height: 0;
}

/* Read-only value with a copy button (store URL). */
.k-copyval { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
.k-copyval__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-copyval__btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-copyval__btn:hover { background: var(--primary-soft); color: var(--primary); }
.k-copyval__said { font-size: var(--text-xs); color: var(--success); white-space: nowrap; }

/* Plan value doubles as the way to go and change it. */
.k-planlink {
  color: inherit;
  text-decoration: none;
  border-radius: var(--radius-sm);
  transition: color var(--duration-fast) var(--ease), background var(--duration-fast) var(--ease);
}
.k-planlink:hover,
.k-planlink:focus-visible {
  color: var(--primary-soft-text);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ── Collapsed user popover ───────────────────────────────────────────────────
   At 60px the rail fits one control, so the avatar is it; identity and sign-out
   live behind it. Portaled to <body>, so it sits on the PAGE surface and takes
   the page's tokens (light or dark), not the ones .k-sidebar re-points for the rail. */
.k-sidebar::-webkit-scrollbar { width: 0; height: 0; }

.k-userpop { position: relative; display: flex; }
.k-userpop__trigger {
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-weight: var(--weight-bold);
}
.k-userpop__panel {
  position: fixed;
  z-index: 60;
  min-width: 13rem;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: 0 16px 40px color-mix(in srgb, var(--shadow-ink) 22%, transparent), 0 4px 12px color-mix(in srgb, var(--shadow-ink) 14%, transparent);
  /* Portaled to <body>, so the page's own tokens apply — light or dark. It
     used to pin --text to ink here, which read as dark-on-dark in dark mode. */
  color: var(--text);
}
.k-userpop__id {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
  min-width: 0;
}
.k-userpop__id:hover { background: var(--surface-hover); }
.k-userpop__signout {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  margin-top: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}
.k-userpop__signout:hover { color: var(--danger); }

/* ── Collapsible rail ─────────────────────────────────────────────────────────
   Desktop only. Labels are UNMOUNTED in JS rather than hidden with opacity, so
   they cannot be read by a screen reader or found by ctrl-F while invisible;
   a title attribute carries the name instead. These rules only handle the
   geometry. Scoped to the desktop query so a drawer on a phone can never
   accidentally render as a rail. */
@media (min-width: 769px) {
  .k-shell--collapsed .k-sidebar { --k-rail-pad: var(--space-2); }
  /* .k-nav-item has no width of its own — as a column-flex child it defaults
     to align-self:stretch, so at collapsed width it was the full rail column
     (~52px) but only ~36px tall (icon + the small vertical padding), giving the
     active background a wide, short rectangle instead of a badge around the
     icon. A fixed, equal width/height plus align-self:center overrides the
     stretch and gives it a true square footprint regardless of the rail's own
     width. */
  .k-shell--collapsed .k-nav-item {
    justify-content: center;
    gap: 0;
    padding: 0;
    width: 2.5rem;
    height: 2.5rem;
    align-self: center;
  }
  .k-shell--collapsed .k-sidebar__brand { align-items: center; padding-left: 0; padding-right: 0; }
  .k-shell--collapsed .k-sidebar__user { justify-content: center; }
  .k-shell--collapsed .k-userpop { justify-content: center; }
  .k-shell--collapsed .k-sidebar__user-info { justify-content: center; }
}

/* The brand row must be full width for the collapse button's margin-left:auto to
   have anything to push against. brand-auth.css sets inline-flex on this class and
   loads after us, so this needs the extra specificity rather than source order. */
.k-sidebar .k-sidebar__logo {
  display: flex;
  width: 100%;
  align-items: center;
}

/* Collapse / expand control in the brand row. */
.k-sidebar__collapse {
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-sidebar__collapse:hover { background: var(--surface-hover); color: var(--text); }

/* When collapsed, the mark itself becomes the expand control: it crossfades to a
   panel icon on hover. Both children are absolutely positioned inside a
   fixed-size button so nothing reflows on hover. */
.k-sidebar__logotoggle {
  position: relative;
  width: 2.25rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text);
}
.k-sidebar__logotoggle > img,
.k-sidebar__logotoggle > svg {
  position: absolute;
  transition: opacity var(--duration-fast) var(--ease);
}
.k-sidebar__logotoggle > svg { opacity: 0; }
.k-sidebar__logotoggle:hover > img,
.k-sidebar__logotoggle:focus-visible > img { opacity: 0; }
.k-sidebar__logotoggle:hover > svg,
.k-sidebar__logotoggle:focus-visible > svg { opacity: 1; }

/* ── Scrollbars ────────────────────────────────────────────────────────────────
   Thin and on-brand. The sidebar was the visible offender on 16:9 laptops, where
   the nav just exceeds the viewport, but this covers any scroll container. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-full);
  /* inset the thumb so it reads as a slim pill, not a full-width bar */
  border: 2px solid transparent;
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }
*::-webkit-scrollbar-corner { background: transparent; }

/* Brand block */
.k-sidebar__brand {
  display: flex;
  flex-direction: column;
  padding: var(--space-2) var(--space-3) var(--space-5);
  gap: var(--space-1);
  position: relative;
}
.k-sidebar__logo {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--primary);
  letter-spacing: -0.02em;
  line-height: 1;
}
.k-sidebar__store-name {
  font-size: var(--text-sm);
  color: var(--text-muted);
  font-weight: var(--weight-normal);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Mobile-only close button — hidden on desktop */
.k-sidebar__close { display: none; }

/* Nav */
.k-sidebar__nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
/* A row is a link, or a button for a coming-soon entry, so it resets a
   button's chrome too. */
.k-nav-item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  min-height: 2rem;
  padding: 0.3125rem 0.625rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-weight: var(--weight-medium);
  font-size: var(--text-md);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.k-nav-item:hover { background: var(--surface-hover); color: var(--text); }
.k-nav-item:focus-visible { outline: 2px solid var(--rail-accent); outline-offset: -2px; }
.k-nav-item[aria-current="page"],
.k-nav-item--active {
  /* A lighter fill, a cream label and a citron icon carry the active state. It
     stays legible at the 60px rail, so no left marker is needed. */
  background: var(--primary-soft);
  color: var(--primary-soft-text);
  font-weight: var(--weight-semibold);
}
/* One of its sub-items is the page: the parent stays lit, the child carries the fill. */
.k-nav-item--open { color: var(--text); font-weight: var(--weight-semibold); }
/* --rail-icon: a palette's dark mode tints the icons with its signature while
   the faint TEXT (search, Soon labels) stays a soft white. */
.k-nav-item .k-nav-item__icon { color: var(--rail-icon, var(--text-faint)); }
.k-nav-item:hover .k-nav-item__icon { color: var(--text-muted); }
/* --rail-active-icon: a palette whose dark pill is pale (gold, grey) swaps
   citron for a colour that reads on it (palettes.ts). */
.k-nav-item--active .k-nav-item__icon { color: var(--rail-active-icon, var(--rail-accent)); }
.k-nav-item--open .k-nav-item__icon { color: var(--rail-accent); }
/* Icons never squash while the rail animates. */
.k-nav-item > svg { flex: none; }
/* Anchor for the collapsed badge dot. */
.k-nav-item__icon { position: relative; display: inline-flex; flex: none; }
.k-nav-item__dot {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--citron);
  box-shadow: 0 0 0 2px var(--rail);
}
.k-nav-item__dot--attn { background: var(--warning); }

/* User block at sidebar bottom */
.k-sidebar__user {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.k-sidebar__user-info {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
/* Holds either an uploaded image or the initial letter. --weight-bold, not the
   --weight-semibold this used to reference: that token was never defined. */
.k-sidebar__user-avatar {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-full);
  overflow: hidden;
  flex-shrink: 0;
  background: var(--primary-soft);
  color: var(--primary-soft-text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
.k-sidebar__user-text { display: flex; flex-direction: column; min-width: 0; }
.k-sidebar__user-name {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.k-sidebar__user-email {
  font-size: var(--text-xs);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.k-sidebar__user-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
/* User block doubles as the Account link */
/* Horizontal padding wider than vertical: the hover backdrop was hugging the
   avatar/text so tightly it read as clipped rather than a deliberate hit area.
   Negative margin cancels the padding's effect on layout, so the row's
   position relative to the sign-out button next to it does not shift. */
a.k-sidebar__user-info {
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-md);
  /* Each side's hover-halo width equals that side's own padding, regardless of
     margin — margin only controls how much layout footprint this item hands to
     its sibling (the sign-out button). Paired margin = -padding keeps that
     footprint at net zero on every side, so the sign-out button's position
     never moves no matter how these numbers change.
     padding-left(8)/margin-left(-2) nets a +6px rightward shift of the actual
     avatar+text content — margin-left is 6px less negative than the -8 that
     would leave content exactly where it started. padding-right(16) doubles
     the previous 8px hover halo on the right specifically. */
  padding: 4px 16px 4px 8px;
  margin: -4px -16px -4px -2px;
  transition: background 0.12s ease;
}
a.k-sidebar__user-info:hover { background: var(--surface-hover); }

/* ── Account page ────────────────────────────────────────────── */
.k-account { display: flex; flex-direction: column; gap: var(--space-4); }
.k-account__avatar {
  width: 64px; height: 64px;
  border-radius: 9999px;
  background: var(--primary-soft);
  color: var(--primary-soft-text);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--text-xl); font-weight: var(--weight-semibold);
  overflow: hidden; flex-shrink: 0;
}
.k-account__avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ── Team (settings) ─────────────────────────────────────────── */
.k-team__list { display: flex; flex-direction: column; gap: var(--space-2); }
.k-team__row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); }
.k-team__row--pending { background: var(--surface-sunken); }
.k-team__avatar {
  width: 36px; height: 36px; border-radius: 9999px;
  background: var(--primary-soft); color: var(--primary-soft-text);
  display: flex; align-items: center; justify-content: center;
  font-weight: var(--weight-semibold); font-size: var(--text-sm);
  overflow: hidden; flex-shrink: 0;
}
.k-team__avatar img { width: 100%; height: 100%; object-fit: cover; }
.k-team__avatar--pending { background: var(--surface-hover); color: var(--text-muted); }
.k-team__who { flex: 1; min-width: 0; }
.k-team__name { font-size: var(--text-sm); font-weight: var(--weight-medium); display: flex; align-items: center; gap: var(--space-2); }
.k-team__email { font-size: var(--text-xs); color: var(--text-muted); }
.k-team__you { font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--primary); background: var(--primary-soft); padding: 1px 7px; border-radius: 999px; }
.k-team__role { font-size: var(--text-xs); color: var(--text-muted); text-transform: capitalize; }
.k-team__invite { margin-top: var(--space-8); display: flex; flex-direction: column; gap: var(--space-3); }
.k-sidebar__signout {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  padding: var(--space-1);
  border-radius: var(--radius-sm);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  line-height: 1;
}
.k-sidebar__signout:hover { color: var(--citron); }

/* .k-main-bg is the actual grid item (the sidebar's 1fr sibling) — full
   width of the column, so the ambient tint/squiggle it paints (brand-auth.css)
   spans edge to edge on any monitor. .k-main is nested one level inside it
   and does the readable-width capping; splitting the two means capping
   content no longer also caps the background painted behind it. */
.k-main-bg {
  /* Without this, wide children (tables) can force the grid track open
     instead of scrolling inside their own wrapper. */
  min-width: 0;
  /* Scroll inside the inset panel so its frame and corners stay on screen.
     Sticky page controls now stick to this panel's scrollport. */
  margin: var(--space-2) var(--space-2) var(--space-2) 0;
  border-radius: var(--radius-md);
  overflow: auto;
  overscroll-behavior: contain;
  min-height: 0;
}
.k-main {
  padding: var(--space-8);
  /* Cap the content column on large displays. The fluid root grows type on 2K/4K;
     without a cap, line length grows with it and the page becomes unreadable.
     --shell is min(1760px, 95vw) — see tokens.css. */
  max-width: var(--shell);
  margin-inline: auto;
  width: 100%;
}

/* Mobile header bar — hidden on desktop */
.k-mobile-header { display: none; }

/* Backdrop — hidden by default */
.k-shell__backdrop { display: none; }

/* Grid utilities */
.k-grid { display: grid; gap: var(--space-4); }
.k-grid--2 { grid-template-columns: repeat(2, 1fr); }
.k-grid--3 { grid-template-columns: repeat(3, 1fr); }

.k-page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border,#e4d9ce);
}
.k-page-header__main { min-width: 0; }
.k-page-header__title { font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: -0.02em; }
/* Deliberately quieter than the title it sits under: one step down from body
   size, and muted a touch further with opacity so the pairing reads as
   title-then-caption rather than two competing lines. */
.k-page-header__sub { color: var(--text-muted); opacity: 0.75; margin-top: 2px; font-size: var(--text-sm); }
.k-page-header__aside { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; justify-content: flex-end; }
.k-page-header__meta { color: var(--text-muted); font-size: var(--text-sm, 0.875rem); white-space: nowrap; }

/* Stays a ROW on a phone, deliberately wrapping rather than stacking.
   `flex-direction: column` + `align-items: stretch` was tried here and broke
   Home: .k-period (the 7/30-day toggle) is a direct child of this element, not
   of __aside, so "stretch" applied to it too — it dropped below the greeting and
   went full width. Wrapping gives Orders' heavier action group its own line when
   it genuinely needs one, and leaves a small control that already fits sitting
   right-aligned on the title's row, which is where it belongs. */
@media (max-width: 640px) {
  .k-page-header { flex-wrap: wrap; gap: var(--space-3); }
  .k-page-header__title { font-size: var(--text-xl); }
}

@media (max-width: 768px) {
  /* Sidebar hidden on mobile by default; overlay on menu-open */
  .k-shell {
    grid-template-columns: 1fr;
    /* REQUIRED, not cosmetic. At this width the sidebar is position:fixed, so the
       grid has exactly two in-flow children: .k-mobile-header and .k-main-bg. With
       only implicit auto rows, grid's default align-content:stretch splits the
       available viewport height EQUALLY between them — so on any page whose
       content is shorter than the viewport the header balloons to ~half the
       screen, with the burger and logo floating in the middle of it (the header
       is align-items:center). Pinning the rows keeps the header at content
       height and gives the remainder to main. */
    grid-template-rows: auto minmax(0, 1fr);
  }
  .k-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: min(20rem, 88vw);
    height: 100dvh;
    z-index: 100;
    box-shadow: var(--shadow-lg);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform var(--duration-normal) var(--ease), visibility 0s var(--duration-normal);
  }
  .k-shell--menu-open .k-sidebar {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--duration-normal) var(--ease);
  }

  /* Show close button inside sidebar on mobile */
  .k-sidebar__close {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    padding: var(--space-1);
    border-radius: var(--radius-sm);
    line-height: 1;
  }
  .k-sidebar__close:hover { color: var(--text); background: var(--surface-hover); }

  /* Backdrop when menu is open */
  .k-shell--menu-open .k-shell__backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: var(--overlay);
    -webkit-backdrop-filter: blur(var(--overlay-blur));
    backdrop-filter: blur(var(--overlay-blur));
    z-index: 90;
  }

  /* Mobile top bar */
  .k-mobile-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    /* The rail's colour — a white bar above a dark drawer read as a seam.
       Re-point the same semantic tokens the sidebar does: descendants (notably
       .k-mobile-header__logo, which is color:var(--text)) would otherwise render
       dark text on the dark bar. */
    --text: var(--rail-text);
    --text-muted: var(--rail-label);
    --surface-hover: var(--rail-hover);
    background: var(--rail);
    color: var(--text);
    border-bottom: 1px solid var(--rail-line);
    position: sticky;
    top: 0;
    z-index: 10;
  }
  .k-mobile-header__toggle {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text);
    display: flex;
    align-items: center;
    padding: 0;
  }
  .k-mobile-header__logo {
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    color: var(--primary);
    letter-spacing: -0.02em;
  }

  /* No frame on a phone: the panel runs edge to edge under the header. */
  .k-main-bg { margin: 0; border-radius: 0; min-height: 0; }

  /* Extra room at the bottom everywhere — almost every page's last card/table
     was landing right on the 1rem edge with nothing below it. */
  .k-main { padding: var(--space-4) var(--space-4) var(--space-8); }
  .k-grid--3 { grid-template-columns: 1fr; }
}

/* ============================================================ utilities (tiny, deliberate) */
.k-stack { display: flex; flex-direction: column; gap: var(--space-4); }
.k-row { display: flex; align-items: center; gap: var(--space-3); }
.k-row--between { justify-content: space-between; }
.k-grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
@media (max-width: 640px) { .k-grid-2 { grid-template-columns: 1fr; } }
.k-muted { color: var(--text-muted); }
.k-faint { color: var(--text-faint); font-size: var(--text-sm); }

/* ============================================================ onboarding booklet layout */
/* Layout-critical rules live here (not inline) so media queries can override them. */
.k-onboarding {
  display: grid;
  grid-template-columns: 2fr 3fr;
  min-height: 100vh;
}
.k-onboarding__panel {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}

@media (max-width: 768px) {
  .k-onboarding {
    grid-template-columns: 1fr;
  }
  .k-onboarding__panel {
    position: static;
    height: auto;
    padding: var(--space-4) var(--space-5);
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
  }
  /* Hide illustration on mobile — just keep logo + dots in the top banner */
  .k-onboarding__illustration { display: none; }
}

/* ============================================================ settings page */
.k-settings {
  display: grid;
  grid-template-columns: 13.5rem 1fr;
  gap: var(--space-6);
  align-items: start;
  /* No cap of its own. The old 53.75rem left roughly half a 1080p screen empty
     once the nav was subtracted. .k-main already caps at --shell, which is the
     right place for it, and the section tabs give the extra width something to do. */
}

/* ── Nav ─────────────────────────────────────────────────────── */
.k-settings__nav {
  background: var(--surface);
  /* Matches the section cards: no outer border, lifted with a shadow instead. */
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-3) var(--space-2);
  position: sticky;
  top: var(--space-6);
}

/* Group entry in the left rail. These are the primary navigation now — sections
   moved to tabs on the right — so they read as bigger, bolder items rather than
   the small uppercase category labels they were. */
.k-settings__nav-heading {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
  color: var(--text-muted);
  background: none;
  border: 0;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-settings__nav-heading:hover { background: var(--primary-soft); color: var(--primary-soft-text); }
.k-settings__nav-heading--active { background: var(--primary-soft); color: var(--primary-soft-text); }

/* Section tabs, right pane. Same icon+label pairing the old nav rows had. */
.k-settings__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2);
}
/* A group with one section has nothing to switch between. */
.k-settings__tabs--single { display: none; }
.k-settings__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
/* Casal-tinted, not the coral --primary-soft the group rail uses — the tab
   strip's own accent (the icon chips) is casal, so its selected/hover state
   matches rather than fighting it. */
.k-settings__tab:hover { background: var(--secondary-soft); color: var(--secondary-text); }
.k-settings__tab--active { background: var(--secondary-soft); color: var(--secondary-text); }
.k-settings__tab .k-settings__nav-icon {
  width: 1.625rem;
  height: 1.625rem;
  border-radius: var(--radius-sm);
  flex: none;
}
/* One accent (casal), not a per-tab rotation — reviewed and the multicolour
   strip did not work. Casal rather than coral so the icon chip reads as its own
   signal, distinct from the coral "selected" state below. */
.k-settings__tab .k-settings__nav-icon { background: var(--secondary-soft); color: var(--secondary-text); }
.k-settings__tab--active .k-settings__nav-icon { background: var(--feature); color: var(--highlight); }

.k-settings__nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: 9px var(--space-3);
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  text-align: left;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-settings__nav-item:hover {
  background: var(--surface-hover);
  color: var(--text);
}
.k-settings__nav-item--active {
  background: var(--primary-soft);
  color: var(--primary-soft-text);
}

.k-settings__nav-badge {
  margin-left: auto;
  font-size: 11px;
  font-weight: var(--weight-semibold);
  background: var(--primary);
  color: #fff;
  border-radius: 999px;
  padding: 1px 6px;
  min-width: 18px;
  text-align: center;
  line-height: 1.6;
}

/* ── Content ─────────────────────────────────────────────────── */
.k-settings__content {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* ── Section card ────────────────────────────────────────────── */
.k-settings__section {
  background: var(--surface);
  /* No outer border — a soft shadow lifts the card off the warm page instead.
     Internal dividers (section header / footer rules) are untouched. */
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.k-settings__section-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5);
  border-bottom: 1px solid var(--border);
}

.k-settings__section-title-group {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}
.k-settings__section-title-group > div:last-child { min-width: 0; }

.k-settings__section-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: var(--primary-soft);
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.k-settings__section-body {
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* ── Read fields ─────────────────────────────────────────────── */
.k-read-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
.k-read-grid--2 {
  grid-template-columns: 1fr 1fr;
}

.k-read-field__label {
  font-size: 11px;
  font-weight: var(--weight-semibold);
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 3px;
}
.k-read-field__value {
  font-size: var(--text-sm);
  color: var(--text);
  line-height: 1.5;
}
.k-read-field__value--empty {
  color: var(--text-faint);
  font-style: italic;
}

/* ── Payment method chips (read view) ────────────────────────── */
.k-payment-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.k-payment-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  border: 1px solid var(--border);
  background: var(--surface-hover);
  color: var(--text);
}
.k-payment-chip--enabled {
  border-color: var(--primary);
  background: var(--primary-soft);
  color: var(--primary-soft-text);
}
.k-payment-chip__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
  flex-shrink: 0;
}
.k-payment-chip--enabled .k-payment-chip__dot {
  opacity: 1;
}

/* ── Form section (edit mode) ────────────────────────────────── */
.k-settings__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.k-settings__form-footer {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-1);
}
.k-settings__form-error {
  font-size: var(--text-sm);
  color: var(--danger);
  flex: 1;
}

/* ── Rework: grouped nav + registry fields + sticky save ─────── */
.k-settings__nav-group { display: flex; flex-direction: column; gap: 2px; }
.k-settings__nav-group + .k-settings__nav-group { margin-top: var(--space-3); }
.k-settings__nav-icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--text-muted); }
.k-settings__nav-item--active .k-settings__nav-icon { color: var(--primary); }

.k-settings__section-title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text);
  display: flex; align-items: center; gap: var(--space-2);
}
.k-settings__section-desc {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}

/* form grid: half → one cell, everything else spans the row */
.k-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}
.k-form-grid .full { grid-column: 1 / -1; }
.k-form-cell { min-width: 0; }

/* switch row */
.k-switch-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.k-switch-row__label { font-size: var(--text-sm); color: var(--text); font-weight: var(--weight-medium); }
/* switch row with a sub-hint under the label (Assistant → Escalation rules) */
.k-switch-row__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.k-switch-row__sub { font-size: var(--text-xs); color: var(--text-muted); line-height: 1.5; }
/* locked row: an always-on rule, not a broken toggle */
.k-switch-row--locked { background: var(--surface-hover); border-style: dashed; }
.k-lock-badge {
  font-size: 10px; font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: 0.06em;
  padding: 2px 7px; border-radius: 999px; background: var(--surface); color: var(--text-muted);
  border: 1px solid var(--border); vertical-align: middle; margin-left: var(--space-2);
}

/* order-notification toggle groups (Settings → Order notifications) */
.k-notify-group { display: flex; flex-direction: column; gap: var(--space-3); }
.k-notify-group + .k-notify-group { margin-top: var(--space-5); }
.k-notify-group__title { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text); }
.k-notify-group__note { font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px; line-height: 1.5; }
.k-notify-group__hint { font-size: var(--text-xs); color: var(--text-muted); font-style: italic; }
.k-notify-toggles { display: flex; flex-direction: column; gap: var(--space-2); }

/* ── WhatsApp templates (Channels → Templates) ──────────────────────────────── */
.k-tpl { display: flex; flex-direction: column; gap: var(--space-4); }
.k-tpl__toolbar { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.k-tpl__title { font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--text); }
.k-tpl__subtitle { margin-top: 2px; max-width: 56ch; }
.k-tpl__actions { display: flex; gap: var(--space-2); flex-shrink: 0; }
.k-tpl__err { font-size: var(--text-sm); color: var(--danger-text, var(--danger)); background: var(--danger-soft, color-mix(in srgb, var(--danger) 10%, transparent)); border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); }
.k-tpl__loading { display: flex; justify-content: center; padding: var(--space-8); }

.k-tpl__list { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.k-tpl-card { display: flex; flex-direction: column; gap: var(--space-2); border-radius: var(--radius-lg); background: var(--surface); box-shadow: var(--shadow-sm); padding: var(--space-4); }
.k-tpl-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.k-tpl-card__name { font-family: var(--font-mono, ui-monospace, "SFMono-Regular", Menlo, monospace); font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text); word-break: break-all; }
.k-tpl-card__meta { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.k-tpl-card__header { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text); }
.k-tpl-card__body { font-size: var(--text-sm); color: var(--text); white-space: pre-wrap; }
.k-tpl-card__footer { font-size: var(--text-xs); color: var(--text-muted); }
.k-tpl-card__reject { font-size: var(--text-xs); color: var(--danger-text, var(--danger)); }
.k-tpl-card__foot { margin-top: auto; display: flex; justify-content: flex-end; }

/* ── Order template catalog (Channels → Templates) ──────────────────────────── */
.k-tplc { display: flex; flex-direction: column; gap: var(--space-4); }
.k-tplc__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.k-tplc__title { font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--text); }
.k-tplc__subtitle { margin-top: 2px; max-width: 60ch; }
.k-tplc__actions { display: flex; gap: var(--space-2); flex-shrink: 0; }
.k-tplc__note {
  display: flex; gap: var(--space-2); align-items: flex-start;
  font-size: var(--text-sm); color: var(--text-muted);
  background: var(--primary-soft, color-mix(in srgb, var(--primary) 8%, var(--surface)));
  border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent);
  border-radius: var(--radius-md); padding: var(--space-3);
}
.k-tplc__note-icon { color: var(--primary); flex-shrink: 0; margin-top: 1px; }
.k-tplc__list { display: flex; flex-direction: column; gap: var(--space-2); }

.k-tplc-row { border-radius: var(--radius-lg); background: var(--surface); box-shadow: var(--shadow-sm); overflow: hidden; }
.k-tplc-row__main { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); width: 100%; text-align: left; background: none; border: 0; cursor: pointer; color: inherit; }
.k-tplc-row__main:hover { background: var(--surface-hover, color-mix(in srgb, var(--text) 3%, var(--surface))); }
.k-tplc-row__icon {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: var(--radius-full);
  display: flex; align-items: center; justify-content: center;
  background: var(--primary-soft, color-mix(in srgb, var(--primary) 12%, var(--surface)));
  color: var(--primary);
}
.k-tplc-row__text { flex: 1; min-width: 0; }
.k-tplc-row__label { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text); }
.k-tplc-row__desc { font-size: var(--text-xs); color: var(--text-muted); margin-top: 1px; }
.k-tplc-row__right { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.k-tplc-row__chev { color: var(--text-muted); transition: rotate 0.15s ease; }
.k-tplc-row__chev--open { rotate: 180deg; }
.k-tplc-row__preview { padding: 0 var(--space-4) var(--space-4) calc(var(--space-4) + 38px + var(--space-3)); }
.k-tplc-row__reject { font-size: var(--text-xs); color: var(--danger-text, var(--danger)); padding: 0 var(--space-4) var(--space-3) calc(var(--space-4) + 38px + var(--space-3)); }
.k-tplc__custom-title { font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin-top: var(--space-2); }
.k-tplc__footer { display: flex; justify-content: center; padding-top: var(--space-2); }

/* create modal form */
.k-tpl-form { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.k-tpl-form__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.k-tpl-form__inserter { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); flex-wrap: wrap; }
.k-tpl-form__inserter .k-muted { font-size: var(--text-xs); }
.k-tpl-form__samples { display: flex; flex-direction: column; gap: var(--space-3); border-left: 2px solid var(--border); padding-left: var(--space-3); }
.k-tpl-form__samples-title { font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.k-tpl-form__preview-label { font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: var(--space-2); }
.k-tpl-bubble { background: var(--primary-soft, color-mix(in srgb, var(--primary) 12%, var(--surface))); border-radius: var(--radius-md); padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-1); max-width: 80%; }
.k-tpl-bubble__header { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text); }
.k-tpl-bubble__body { font-size: var(--text-sm); color: var(--text); white-space: pre-wrap; }
.k-tpl-bubble__footer { font-size: var(--text-xs); color: var(--text-muted); }

/* option cards (radio / multiselect toggle) */
.k-optset { display: flex; flex-direction: column; gap: var(--space-2); }
.k-opt {
  display: flex; align-items: flex-start; gap: var(--space-3);
  width: 100%; text-align: left; cursor: pointer;
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface);
  transition: border-color var(--duration-fast) var(--ease), background var(--duration-fast) var(--ease);
}
.k-opt:hover { border-color: var(--text-faint); }
.k-opt--active { border-color: var(--primary); background: var(--primary-soft); }
.k-opt__mark {
  width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px;
  border: 2px solid var(--border); background: transparent;
  display: flex; align-items: center; justify-content: center;
}
.k-opt__mark--radio { border-radius: 50%; }
.k-opt__mark--check { border-radius: 5px; }
.k-opt--active .k-opt__mark { border-color: var(--primary); background: var(--primary); }
.k-opt__body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.k-opt__label { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text); }
.k-opt--active .k-opt__label { color: var(--primary-soft-text); }
.k-opt__sub { font-size: var(--text-xs); color: var(--text-muted); }
.k-opt__badge {
  font-size: var(--text-xs); white-space: nowrap; align-self: center;
  padding: 2px 8px; border-radius: 999px;
  background: var(--surface-hover); color: var(--text-muted);
}
.k-opt--active .k-opt__badge { background: var(--primary-soft); color: var(--primary); }
/* A deal worth shouting about ("Save Rs 2,900"): the palette's highlight, picked or not. */
.k-opt__badge--highlight,
.k-opt--active .k-opt__badge--highlight { background: var(--highlight); color: var(--on-highlight); font-weight: var(--weight-semibold); }

/* sub-panel (bank details) + info note */
.k-subpanel {
  padding: var(--space-4); background: var(--surface-hover);
  border-radius: var(--radius-md); display: flex; flex-direction: column; gap: var(--space-3);
}
.k-subpanel__title { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text); }
.k-note {
  padding: var(--space-3) var(--space-4); background: var(--surface-hover);
  border-radius: var(--radius-md); font-size: var(--text-xs); color: var(--text-muted); line-height: 1.6;
}

/* always-bottom floating "island" save bar */
.k-savebar {
  position: fixed;
  left: var(--k-rail); right: 0; bottom: var(--space-5);
  z-index: 20;
  display: flex; justify-content: center;
  padding: 0 var(--space-4);
  pointer-events: none;            /* gutter lets clicks through; island re-enables below */
}
.k-savebar__inner {
  pointer-events: auto;
  width: 100%; max-width: 35rem;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow:
    0 10px 30px -6px rgba(0, 0, 0, 0.18),
    0 2px 8px rgba(0, 0, 0, 0.08);
}
.k-savebar__msg { font-size: var(--text-sm); color: var(--text-muted); }
.k-savebar__error { color: var(--danger); }
.k-savebar__actions { display: flex; gap: var(--space-2); flex-shrink: 0; }
.k-savebar-spacer { height: 76px; }

/* nav: right-side group (count badge + unsaved dot) */
.k-settings__nav-right { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; }
.k-settings__nav-right .k-settings__nav-badge { margin-left: 0; }
.k-settings__nav-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--primary); flex-shrink: 0; }

/* inline text button (e.g. reset to template) */
.k-link-btn {
  align-self: flex-start;
  background: none; border: none; padding: 0; margin-top: var(--space-1);
  color: var(--primary); font-size: var(--text-xs); font-weight: var(--weight-medium); cursor: pointer;
}
.k-link-btn:hover { text-decoration: underline; }

/* Orders bulk select + print */
.k-bulkbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-4); margin-bottom: var(--space-3);
  background: var(--primary-soft); border: 1px solid var(--primary); border-radius: var(--radius-md);
}
.k-bulkbar__count { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--primary-soft-text); }
.k-bulkbar__actions { display: flex; align-items: center; gap: var(--space-2); }
.k-orders-table__check { width: 36px; text-align: center; }

/* KYC / verification */
.k-verif {
  margin-left: var(--space-2);
  font-size: 10px; font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: 0.06em;
  padding: 2px 8px; border-radius: 999px; vertical-align: middle;
}
.k-verif--unverified { background: var(--surface-hover); color: var(--text-muted); }
.k-verif--pending { background: var(--warning-bg); color: var(--warning-text); }
.k-verif--verified { background: var(--success-bg); color: var(--success-text); }
.k-kyc-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-3); }

/* Logo upload (Documents & branding) */
.k-logo-row { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; }
.k-logo-preview {
  width: 96px; height: 96px; flex-shrink: 0; overflow: hidden;
  border-radius: var(--radius-md); box-shadow: var(--shadow-sm);
  background: var(--surface-hover); display: flex; align-items: center; justify-content: center;
}
.k-logo-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.k-logo-preview__empty { font-size: var(--text-xs); color: var(--text-faint); }
.k-logo-actions { display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start; }
.k-logo-hint { font-size: var(--text-xs); color: var(--text-muted); margin: 0; max-width: 22rem; }

/* Assistant page */
.k-assist { max-width: 45rem; display: flex; flex-direction: column; gap: var(--space-4); }
.k-soon-badge {
  font-size: 10px; font-weight: var(--weight-semibold); text-transform: uppercase; letter-spacing: 0.06em;
  padding: 2px 7px; border-radius: 999px; background: var(--surface-hover); color: var(--text-muted);
  vertical-align: middle;
}
.k-mirror { display: flex; flex-direction: column; }
.k-mirror__row {
  display: grid; grid-template-columns: 120px 1fr auto; align-items: baseline; gap: var(--space-3);
  padding: var(--space-3) 0;
}
.k-mirror__row + .k-mirror__row { border-top: 1px solid var(--border); }
.k-mirror__label {
  font-size: 11px; font-weight: var(--weight-semibold); color: var(--text-faint);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.k-mirror__value { font-size: var(--text-sm); color: var(--text); min-width: 0; }
.k-mirror__link { font-size: var(--text-xs); color: var(--primary); text-decoration: none; font-weight: var(--weight-medium); }
.k-mirror__link:hover { text-decoration: underline; }

/* ============================================================ drawer */
/*
 * Right-side slide-in panel. Uses <dialog> for native focus trap + backdrop.
 * Widths: default = 480px, --wide = 680px.
 *
 * Usage:
 *   <dialog class="k-drawer" ref={...}>
 *     <div class="k-drawer__header">…</div>
 *     <div class="k-drawer__body">…</div>
 *     <div class="k-drawer__footer">…</div>
 *   </dialog>
 */
.k-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  margin: 0;
  padding: 0;
  width: min(480px, 100vw);
  max-width: 100vw;
  height: 100dvh;
  /* Override the UA dialog:modal default (max-height: calc(100% - 2em - 6px)),
     which otherwise caps the panel ~38px short and leaves a gap at the bottom. */
  max-height: 100dvh;
  border: 0;
  border-left: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: -4px 0 24px rgba(0,0,0,.10);
  /* entry animation */
  translate: 100% 0;
  transition: translate 260ms var(--ease), display 260ms allow-discrete, overlay 260ms allow-discrete;
}
/* `none`, not `0 0`. Any other value makes the open drawer the containing block
   for its `position: fixed` descendants, and those are placed in VIEWPORT
   coordinates: InfoTip/Tooltip bubbles and the upgrade-lock tooltip landed off
   to the right and were clipped by `overflow: hidden`, so no tip in a drawer
   ever showed. `none` still animates the slide (it interpolates as 0 0). */
.k-drawer[open] {
  translate: none;
}
@starting-style { .k-drawer[open] { translate: 100% 0; } }

.k-drawer--wide { width: min(680px, 100vw); }

.k-drawer::backdrop {
  background: var(--overlay);
  -webkit-backdrop-filter: blur(var(--overlay-blur));
  backdrop-filter: blur(var(--overlay-blur));
  opacity: 0;
  transition: opacity 260ms var(--ease), display 260ms allow-discrete, overlay 260ms allow-discrete;
}
.k-drawer[open]::backdrop { opacity: 1; }
@starting-style { .k-drawer[open]::backdrop { opacity: 0; } }

.k-drawer__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.k-drawer__title {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  flex: 1;
  min-width: 0;
}
.k-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 1.25rem;
  line-height: 1;
  flex-shrink: 0;
}
.k-drawer__close:hover { background: var(--surface-hover); color: var(--text); }

.k-drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.k-drawer__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}
/* Footer action row. One group when there is only a commit pair; two when the
   drawer also carries record actions (duplicate / archive), which sit left,
   away from the primary. Replaces per-drawer inline flex styling. */
.k-drawer__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
  width: 100%;
}
.k-drawer__actions--split { justify-content: space-between; }
.k-drawer__actions-group { display: flex; align-items: center; gap: var(--space-2); }
/* Record actions rendered as .k-action-link rather than buttons — they need
   more air between them than two adjacent pills do. */
.k-drawer__actions-group--quiet { gap: var(--space-4); }

/* Text + icon action. For operations on the RECORD (duplicate, archive) sitting
   next to the commit buttons: same tap target, none of the visual weight, so the
   primary stays the loudest thing in the footer. */
.k-action-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-2) 0;
  border: none;
  background: none;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease);
}
.k-action-link:hover:not(:disabled) { color: var(--text); }
.k-action-link--danger:hover:not(:disabled) { color: var(--danger); }
.k-action-link:disabled { opacity: 0.45; cursor: not-allowed; }
.k-action-link:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; border-radius: var(--radius-sm); }
.k-action-link svg { flex-shrink: 0; }

@media (max-width: 560px) {
  /* column-reverse, so with DOM order [record actions, commit pair] the commit
     pair ends up on top: Save is the reason the drawer is open, and Archive
     should not be the button nearest the thumb. */
  .k-drawer__actions { flex-direction: column-reverse; align-items: stretch; }
  .k-drawer__actions-group { width: 100%; }
  .k-drawer__actions-group .k-btn { flex: 1; }
  /* The text links stay on one centred line — stretching them edge to edge would
     turn them back into the fake buttons they were meant to stop being. */
  .k-drawer__actions-group--quiet { justify-content: center; gap: var(--space-5); }
}

.k-drawer__footer-error {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--danger);
  text-align: center;
}

/* Drawer sections (used by Discounts / Collections drawers) — same rhythm as .k-form-section */
.k-drawer__section {
  padding: var(--space-5);
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.k-drawer__section:first-child { border-top: none; }
.k-drawer__section-title {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin: 0;
}

/* ============================================================ product catalog */
.k-product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-4);
}
/* auto-fill's 220px minimum only ever fits one column on a phone — force two,
   same square-thumbnail-plus-truncated-title card just narrower. */
@media (max-width: 640px) {
  .k-product-grid { grid-template-columns: 1fr 1fr; gap: var(--space-3); }
}
.k-product-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow var(--duration-fast) var(--ease);
}
.k-product-card:hover {
  box-shadow: var(--shadow-md);
}
.k-product-card__img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: var(--surface-sunken);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-faint);
  font-size: 2.5rem;
}
.k-product-card__body { padding: var(--space-3); }
.k-product-card__title {
  font-weight: var(--weight-medium);
  font-size: var(--text-sm);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.k-product-card__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.k-product-card__price { font-weight: var(--weight-semibold); color: var(--text); }
.k-product-card__stock { margin-top: var(--space-2); }
.k-product-card__instock { font-size: var(--text-xs); color: var(--text-muted); }

/* image upload zone */
.k-upload-zone {
  border: 2px dashed var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease), background var(--duration-fast) var(--ease);
  color: var(--text-muted);
  font-size: var(--text-sm);
}
.k-upload-zone:hover, .k-upload-zone.is-dragover {
  border-color: var(--primary);
  background: var(--primary-soft);
  color: var(--primary);
}
.k-upload-thumbnails {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.k-upload-thumb {
  position: relative;
  width: 4.5rem;
  height: 4.5rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.k-upload-thumb img { width: 100%; height: 100%; object-fit: cover; }
.k-upload-thumb__remove {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,.45);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity var(--duration-fast);
  cursor: pointer;
  color: #fff;
  font-size: 1rem;
  border: 0;
}
.k-upload-thumb:hover .k-upload-thumb__remove { opacity: 1; }

/* ============================================================ colour picker
   In-page replacement for input[type=color]'s native dialog, which is drawn by
   the browser chrome and cannot be styled at all. See ColorPicker.tsx. */
.k-cpick { display: flex; flex-direction: column; gap: var(--space-3); }

/* The saturation/brightness surface. Three layers: the hue as a flat
   background (set inline), a left-to-right white→transparent wash for
   saturation, and a bottom-up black→transparent wash for brightness. */
.k-cpick__sv {
  position: relative;
  height: 9rem;
  border-radius: var(--radius-md);
  cursor: crosshair;
  /* REQUIRED, not cosmetic: without it a touch drag scrolls the page instead
     of moving the handle, which makes the picker unusable on a phone. */
  touch-action: none;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.1);
}
.k-cpick__sv-sat,
.k-cpick__sv-val {
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.k-cpick__sv-sat { background: linear-gradient(to right, #fff, rgb(255 255 255 / 0)); }
.k-cpick__sv-val { background: linear-gradient(to top, #000, rgb(0 0 0 / 0)); }

.k-cpick__hue {
  position: relative;
  height: 14px;
  border-radius: var(--radius-full);
  cursor: ew-resize;
  touch-action: none;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.1);
  background: linear-gradient(to right,
    #f00 0%, #ff0 16.66%, #0f0 33.33%, #0ff 50%, #00f 66.66%, #f0f 83.33%, #f00 100%);
}

/* Shared by both surfaces — a ring showing the colour under it, so it stays
   visible against black, white and every hue in between. */
.k-cpick__handle {
  position: absolute;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: var(--radius-full);
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.25), var(--shadow-sm);
  pointer-events: none;
}
.k-cpick__sv:focus-visible,
.k-cpick__hue:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ============================================================ size charts drawer */
.k-sizechart-list { margin-top: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
.k-sizechart-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.k-sizechart-row__main { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.k-sizechart-row__icon { display: flex; color: var(--text-muted); flex-shrink: 0; }
.k-sizechart-row__name { font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-sizechart-row__actions { display: flex; gap: var(--space-2); flex-shrink: 0; }

.k-sizechart-form { display: flex; flex-direction: column; gap: var(--space-3); }

/* Table/Image, as two scannable tiles instead of a pair of plain buttons —
   same "pick one of two" language as the product wizard's fork toggle. */
.k-sizechart-toggle { display: flex; gap: var(--space-2); }
.k-sizechart-toggle__opt {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  transition: border-color var(--duration-fast) var(--ease), background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-sizechart-toggle__opt:hover { border-color: var(--primary); }
.k-sizechart-toggle__opt--on { border-color: var(--primary); background: var(--primary); color: #fff; }

.k-sizechart-preview-img { display: block; max-width: 100%; border-radius: var(--radius-md); margin-bottom: var(--space-2); }

.k-sizechart-table__hint { font-size: var(--text-sm); color: var(--text-muted); margin-bottom: var(--space-3); }

/* A spreadsheet, not a stack of generic form fields — borderless inputs that
   sit flush in their cell (only showing a box once focused), a header row
   that reads as a header first and an editable field second, and the "add
   a column/row" controls built into the grid itself (a "+" cell at the end
   of the header, an "add a row" line at the bottom) instead of buttons
   floating below with no visual tie to what they add to. */
.k-sizechart-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-md); }
.k-sizechart-table { border-collapse: collapse; width: 100%; }
.k-sizechart-table thead th {
  background: var(--surface-warm);
  text-align: left;
  padding: var(--space-1);
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
  white-space: nowrap;
}
.k-sizechart-table thead th:first-child { border-top-left-radius: var(--radius-md); }
.k-sizechart-table thead th:last-child { border-top-right-radius: var(--radius-md); border-right: none; }
.k-sizechart-table__colhead { display: flex; align-items: center; gap: 4px; }
.k-sizechart-table__colinput {
  flex: 1;
  min-width: 4rem;
  border: none;
  background: transparent;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
}
.k-sizechart-table__colinput::placeholder { color: var(--text-faint); font-weight: var(--weight-normal); }
.k-sizechart-table__colinput:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; background: var(--surface); }
/* Hidden until the column's own name field is actually focused — clicking
   into "Chest" to rename it is also how you get to delete "Chest"; the icon
   doesn't clutter every column at rest. */
.k-sizechart-table__colremove { display: none; }
.k-sizechart-table__colhead:focus-within .k-sizechart-table__colremove { display: inline-flex; }

.k-sizechart-table tbody td { padding: 2px; border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); vertical-align: middle; }
.k-sizechart-table tbody tr:last-child td { border-bottom: none; }
.k-sizechart-table tbody td:last-child { border-right: none; }
.k-sizechart-table__cellinput {
  width: 100%;
  min-width: 4rem;
  border: none;
  background: transparent;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
}
.k-sizechart-table__cellinput:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; background: var(--surface); }
/* The last cell of each row carries its own remove-row button instead of a
   dedicated trailing column — that column had nothing in its header and
   read as a stray empty one. */
.k-sizechart-table__rowend { display: flex; align-items: center; gap: 2px; }
.k-sizechart-table__rowend .k-sizechart-table__cellinput { flex: 1; min-width: 0; }
.k-sizechart-table__rowremove { flex-shrink: 0; }

/* Kept out of the table on every screen size, not just mobile — a header/
   footer control that scrolls away with the table is easy to lose track of
   regardless of viewport. */
.k-sizechart-table__addgroup { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
.k-sizechart-table__addbtn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-sizechart-table__addbtn:hover { border-color: var(--primary); color: var(--primary); }

/* variant rows inside the drawer */
.k-variant-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.k-variant-table th {
  text-align: left;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  padding: var(--space-2) var(--space-2) var(--space-2) 0;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.k-variant-table td {
  padding: var(--space-2) var(--space-2) var(--space-2) 0;
  border-bottom: 1px solid var(--border-subtle, var(--border));
  vertical-align: middle;
}
.k-variant-table tr:last-child td { border-bottom: 0; }

/* Mobile stand-in for the table above — five columns force horizontal
   scroll on a phone; one card per variant instead, same fields. */
.k-variant-cards { display: none; }
.k-variant-card { background: var(--surface-warm); border-radius: var(--radius-md); padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
.k-variant-card__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.k-variant-card__label { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text); }
.k-variant-card__fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); }
/* Fields sit on white here, not the platform's default grey field-bg — that
   default assumes a white card underneath, and this one's cream. */
.k-variant-card .k-input { background: var(--surface); }
@media (max-width: 680px) {
  .k-variant-table { display: none; }
  .k-variant-cards { display: flex; flex-direction: column; gap: var(--space-2); }
}

/* The per-variant "Active" pill that lived here is now .k-status-toggle--sm —
   the same control as the drawer header, so the two do not look like different
   kinds of switch. It also fixes the pill's own flaw: it read "Active" in both
   states and signalled the difference with colour alone. */

/* stock badge */
.k-stock-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: 2px var(--space-2);
  border-radius: 999px;
}
.k-stock-badge--in   { background: var(--success-bg); color: var(--success-text); }
.k-stock-badge--low  { background: var(--warning-bg); color: var(--warning-text); }
.k-stock-badge--out  { background: var(--danger-bg);  color: var(--danger-text); }
.k-stock-badge--none { background: var(--surface-sunken); color: var(--text-faint); }

/* movement-history timeline (inventory drawer) */
.k-timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.k-timeline__row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}
.k-timeline__row:last-child { border-bottom: none; }
.k-timeline__dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  margin-top: 6px;
}
.k-timeline__main { min-width: 0; }
.k-timeline__line { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.k-timeline__label { font-weight: var(--weight-medium); }
.k-timeline__change { font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; white-space: nowrap; }
.k-timeline__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-top: 2px;
}
.k-timeline__running { white-space: nowrap; font-variant-numeric: tabular-nums; }
.k-timeline__when { font-size: var(--text-xs); color: var(--text-faint); margin-top: 2px; }

/* inventory product grouping (expandable rows) */
.k-inv-group { cursor: pointer; }
.k-inv-group:hover { background: var(--surface-hover); }
.k-inv-chevron {
  display: inline-block;
  width: 1em;
  margin-right: var(--space-2);
  color: var(--text-muted);
  transition: transform 0.15s ease;
}
.k-inv-chevron--open { transform: rotate(90deg); }
.k-inv-variant > td:first-child { padding-left: var(--space-8); }
.k-inv-variant__name { color: var(--text-muted); }

/* FAB (floating action button) */
.k-fab {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 999px;
  background: var(--primary);
  color: #fff;
  border: 0;
  font-size: 1.5rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 16px rgba(0,0,0,.18);
  transition: background var(--duration-fast) var(--ease), box-shadow var(--duration-fast) var(--ease);
  z-index: 50;
}
.k-fab:hover { background: var(--primary-hover); box-shadow: 0 6px 20px rgba(0,0,0,.22); }

/* ============================================================ product drawer — new UX sections */

/* Body variant: sections separated by borders instead of gap */
.k-drawer__body--sections {
  gap: 0;
  padding: 0;
}

/* Section divider */
.k-form-section {
  padding: var(--space-5);
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.k-form-section--first { border-top: none; }
.k-form-section--last  { padding-bottom: var(--space-6); }

.k-form-section__label {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin: 0;
}
.k-form-section__hint {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: calc(-1 * var(--space-2)) 0 0;
}

/* Numbered step badge, opt-in via an explicit span (not a ::before counter,
   so the number is exact and doesn't drift if a section is ever hidden).
   The :has() flips the label itself to a flex row so the badge centers on
   the text's line box exactly, instead of a hand-tuned vertical-align nudge
   — and it only fires for labels that actually carry a badge, so the other
   three pages sharing .k-form-section__label are untouched. */
.k-form-section__label:has(.k-section-num),
.k-order-block__label:has(.k-section-num) { display: flex; align-items: center; }
.k-section-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  margin-right: var(--space-2);
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: var(--weight-bold);
  text-transform: none;
  letter-spacing: normal;
}

/* Small leading icon inside a field — labeling affordance (this is who/where
   a field is for), not a brand accent, so it stays neutral rather than
   coral like the page-level search icon. */
.k-field-icon { position: relative; }
.k-field-icon svg {
  position: absolute;
  top: 50%;
  left: var(--space-3);
  transform: translateY(-50%);
  color: var(--text-faint);
  pointer-events: none;
}
.k-field-icon .k-input { padding-left: 2.25rem; }

/* Payment method as two scannable tiles instead of a dropdown — there are
   only ever two options, and a tile makes the current choice visible without
   opening anything. */
.k-payment-tiles { display: flex; gap: var(--space-2); }
.k-payment-tile {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--text-sm);
  transition: border-color var(--duration-fast) var(--ease), background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-payment-tile:hover { border-color: var(--coral); }
.k-payment-tile--active { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-soft-text); }

/* The borderless-grey field look trialled here first is now the platform
   default (.k-input/.k-select/.k-textarea base, above) — greenlit, so this
   drawer needs no override of its own any more. */
.k-form-section__header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

/* ── Status pill selector ────────────────────────────────────── */
.k-status-pills {
  display: flex;
  gap: var(--space-2);
}
.k-status-pill {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  border: 1.5px solid var(--border-strong);
  background: var(--surface);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  transition: border-color var(--duration-fast) var(--ease),
              background var(--duration-fast) var(--ease),
              color var(--duration-fast) var(--ease);
}
.k-status-pill[aria-selected="true"] {
  border-color: var(--primary);
  background: var(--primary-soft);
  color: var(--primary-soft-text);
}
.k-status-pill:hover:not([aria-selected="true"]) {
  background: var(--surface-hover);
  color: var(--text);
}

/* ── Inventory toggle ────────────────────────────────────────── */
.k-inventory-toggle {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
}
.k-inventory-toggle__label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text);
}
.k-inventory-toggle__hint {
  display: block;
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}

/* ── Tag chip input ──────────────────────────────────────────── */
.k-tags-input {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.5rem;
  /* Fully round, matching .k-input and the rest of the field family. Extra
     horizontal padding because a pill's corners eat into the usable width —
     without it the first chip sits on the curve. */
  padding: var(--space-1) var(--space-3);
  /* The field family's look (STYLE-GUIDE.md: fields): the grey well, no border
     at rest, the coral border and ring on focus. It used to be a white pill
     with a grey outline, the only field that looked like that. */
  background: var(--field-bg);
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  cursor: text;
  transition: border-color var(--duration-fast) var(--ease);
}
.k-tags-input:focus-within {
  outline: 2px solid var(--focus-ring);
  outline-offset: 0;
  border-color: var(--primary);
}
.k-tags-input input {
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: var(--text-md);
  color: var(--text);
  min-width: 6rem;
  flex: 1;
  padding: 0;
}
.k-tags-input input::placeholder { color: var(--text-faint); }

.k-tag-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  /* White on the field's grey well: the chip's fill is its edge, no outline. */
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  padding: 0.125rem var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text);
  white-space: nowrap;
}
.k-tag-chip button {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-faint);
  padding: 0;
  line-height: 1;
  font-size: 0.7em;
  display: flex;
  align-items: center;
}
.k-tag-chip button:hover { color: var(--danger); }

/* ── Option editor ───────────────────────────────────────────── */
.k-options-editor {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.k-option-row {
  background: var(--surface-warm);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.k-option-row__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.k-option-row__name {
  flex: 1;
  background: var(--surface);
}
.k-option-row__remove {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text-muted);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  white-space: nowrap;
  flex-shrink: 0;
}
.k-option-row__remove:hover { color: var(--danger); background: var(--surface-hover); }

.k-option-row__count {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* value chip area */
.k-value-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.25rem;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  /* Transparent, not none — keeps the 1px box so :focus-within below doesn't
     shift layout when it swaps the colour in. */
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  cursor: text;
  transition: border-color var(--duration-fast) var(--ease);
}
.k-value-chips:focus-within {
  outline: 2px solid var(--focus-ring);
  outline-offset: 0;
  border-color: var(--primary);
}

.k-value-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: var(--primary-soft);
  color: var(--primary-soft-text);
  border-radius: var(--radius-full);
  padding: 0.125rem var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}
.k-value-chip button {
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  opacity: 0.6;
  padding: 0;
  line-height: 1;
  font-size: 0.7em;
  display: flex;
  align-items: center;
}
.k-value-chip button:hover { opacity: 1; }

.k-value-chips__input {
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text);
  min-width: 8rem;
  flex: 1;
  padding: 0;
}
.k-value-chips__input::placeholder { color: var(--text-faint); }

.k-add-option-btn {
  background: none;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  color: var(--text-muted);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  transition: border-color var(--duration-fast) var(--ease),
              color var(--duration-fast) var(--ease);
}
.k-add-option-btn:hover {
  border-color: var(--primary);
  color: var(--primary);
}

/* ── Variant table UX improvements ──────────────────────────── */
.k-variant-table-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.k-variant-table-header__count {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
}
.k-bulk-price {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Variant label column (option combination like "Red / M") */
.k-variant-table__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text);
  white-space: nowrap;
  padding-right: var(--space-3) !important;
}

/* ── Responsive ──────────────────────────────────────────────── */
@media (max-width: 640px) {
  .k-mirror__row { grid-template-columns: 1fr auto; }
}

/* Settings — rewritten for the tabs architecture (group rail + per-group tab
   strip), matching the 768px breakpoint the shell itself switches at so there
   is no width range where one has gone mobile and the other has not. */
@media (max-width: 768px) {
  .k-settings {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
  /* Groups become a wrapped row of pills instead of a tall sticky sidebar list
     — there is no vertical room to spare next to the content on a phone, and
     sticky would either float over content or need to duck under the mobile
     header, both fiddlier than just letting it scroll away with the page. */
  .k-settings__nav {
    position: static;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    padding: var(--space-2);
  }
  .k-settings__nav-group { display: contents; }
  .k-settings__nav-heading {
    width: auto;
    font-size: var(--text-sm);
    padding: var(--space-2) var(--space-3);
  }
  .k-settings__section-header,
  .k-settings__section-body { padding: var(--space-4); }
  /* .k-main's own mobile padding-bottom is --space-8 now (see the shared
     shell rule above), which covers what this used to add on its own — no
     separate bottom padding needed here any more. */
  .k-read-grid--2 { grid-template-columns: 1fr; }
  .k-form-grid { grid-template-columns: 1fr; }
  .k-savebar { left: 0; }
  .k-savebar__inner { padding: var(--space-3) var(--space-4); }
}

/* ── M3 Orders ──────────────────────────────────────────────────────────────── */

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

.k-icon-btn {
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  line-height: 1;
}
.k-icon-btn:hover { background: var(--surface-hover); color: var(--text); }
/* Compound selector, not source order — wins over the shared hover above
   regardless of where either rule sits in the file. No hover background on
   a delete action; just the icon itself going red. */
.k-icon-btn.k-sizechart-table__colremove:hover { background: none; color: var(--danger); }

.k-link-sm { font-size: var(--text-sm); color: var(--link); text-decoration: none; }
.k-link-sm:hover { text-decoration: underline; }

/* Orders list table */
/* Orders action strip — work-queue counters above the table */
.k-orderstats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
/* The card itself is gone from here entirely — these buttons now carry
   k-kpi + k-home-kpi (see OrdersPage.tsx) and get their base look, hover,
   selected state and typography from THOSE shared rules, guaranteeing pixel
   parity with Home's cards instead of two sets of hand-matched values that
   drift apart the next time either one changes. Only what Home's cards have
   no equivalent of stays page-specific, below. */
.k-orderstat__sub { font-size: var(--text-xs); color: var(--text-muted); }
.k-orderstat--zero .k-kpi__value { color: var(--text-muted); }

/* Home KPI dashboard — one card, segmented like Shopify. Each segment is a
   button that selects the metric plotted in the chart below; the active one
   gets an underline accent. */
.k-kpis {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.k-kpi {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-5);
  background: transparent;
  border: none;
  border-left: 1px solid var(--border);
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  position: relative;
  transition: background var(--duration-fast) var(--ease);
}
.k-kpi:first-child { border-left: none; }
.k-kpi:hover { background: var(--surface-hover); }
.k-kpi--active { background: var(--surface-sunken); }
.k-kpi--active::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--primary);
}
.k-kpi__label { font-size: var(--text-sm); color: var(--text-muted); }
.k-kpi--active .k-kpi__label { color: var(--text); }
.k-kpi__value { font-size: 1.375rem; font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; line-height: 1.15; }
.k-kpi__foot { margin-top: var(--space-2); }
.k-kpi__delta { font-size: var(--text-xs); font-weight: var(--weight-medium); }
.k-kpi__delta--up { color: var(--success-text); }
.k-kpi__delta--down { color: var(--danger-text); }
.k-kpi__delta--flat { color: var(--text-muted); }

@media (max-width: 720px) {
  .k-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .k-kpi:nth-child(3) { border-left: none; }
  .k-kpi:nth-child(n+3) { border-top: 1px solid var(--border); }
}

/* Home page only — four separate cards instead of one strip divided by
   internal border-lines. Gap does the separating now; each card gets its own
   shadow instead of the strip's single outer border. */
.k-home-kpis {
  background: none;
  border: none;
  border-radius: 0;
  overflow: visible;
  /* The base .k-kpis rule this also carries puts a shadow on the STRIP —
     meaningful when it was one bordered container, dead weight now that each
     card below casts its own. Without this the (now-stronger) --shadow-sm
     doubled up as an outer halo around the whole row. */
  box-shadow: none;
  gap: var(--space-4);
}
.k-home-kpi {
  /* .k-kpi's own background is "transparent" — correct for the old single-strip
     look where the strip itself supplied the white fill, but on a standalone
     card that meant showing whatever sits behind it, which on Home is the
     page canvas (--canvas on .k-main-bg). Explicit
     white restores what "an individual white card" actually asked for. */
  background: var(--surface);
  border-left: none;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.k-home-kpi:hover:not(.k-kpi--active) { background: var(--primary-soft); color: var(--primary-soft-text); }
/* Selected card: flat coral, not a tint — distinct from every other card on
   the page rather than a shade of the same white. Re-pointing --text/--text-muted
   (the same trick the casal sidebar uses) means the label, value and "flat"
   delta all flip to on-coral colours for free; the up/down deltas are pinned
   to white explicitly since --success-text/--danger-text would otherwise stay
   green/red and vanish against solid coral. */
.k-home-kpi.k-kpi--active {
  /* The primary fill and its text: coral and white, unless a dashboard
     palette's accent says otherwise (an amber takes ink text). */
  --text: var(--on-primary);
  --text-muted: color-mix(in srgb, var(--on-primary) 82%, transparent);
  background: var(--primary);
  color: var(--on-primary);
}
.k-home-kpi.k-kpi--active .k-kpi__delta--up,
.k-home-kpi.k-kpi--active .k-kpi__delta--down { color: var(--on-primary); }
/* No bottom bar on the selected card — reviewed, not needed once the whole
   card is already coral. The small citron mark lives on the label's own dot
   instead (below), which every card already has one of. */
.k-home-kpi.k-kpi--active::after { display: none; }
/* Specificity, not "later wins": .k-kpis .k-kpi:nth-child(N) .k-kpi__label::before
   (brand-auth.css, alternating the dot's colour per card) is FOUR class-level
   selectors — .k-kpis, .k-kpi, :nth-child(N) (a pseudo-class, same weight as a
   class), .k-kpi__label — tying this rule's previous form exactly. A tie falls
   back to file order, and brand-auth.css loads after this file, so those rules
   won for cards 2–4; only card 1 (Sales) has no nth-child(1) rule to tie with,
   which is why the citron dot only ever showed up there. Compounding BOTH
   container classes onto one element (.k-kpis.k-home-kpis — the container
   carries both) adds a fifth class-level selector, beating every nth-child
   rule outright regardless of which loads first. */
.k-kpis.k-home-kpis .k-home-kpi.k-kpi--active .k-kpi__label::before { background: var(--citron); }
@media (max-width: 720px) {
  /* The shared mobile rule adds a seam between the two stacked rows to
     replace the divider that used to run between all four; the home variant
     already gets that separation from .k-home-kpis' own gap. */
  .k-home-kpi:nth-child(n+3) { border-top: none; }
}

/* Metric chart — Home's selected KPI day by day (current vs prior period).
   Drawn by apps/dashboard/src/pages/HomeChart.tsx in real pixels: a smooth
   --primary curve over a soft fade, the prior period a dashed
   --secondary-text line; today's unfinished stretch is the faint current. */
.k-chart {
  --chart-cur: var(--primary);
  --chart-prev: var(--secondary-text);
  --chart-gutter: calc(var(--space-12) + var(--space-2)); /* 56px; GUTTER in HomeChart.tsx */
  background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: var(--space-3) var(--space-4) var(--space-4);
}
.k-chart__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
.k-chart__legend { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); }
.k-chart__key { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-xs); color: var(--text-muted); }
.k-chart__swatch { width: 14px; height: 0; border-top: 2px solid var(--chart-cur); display: inline-block; }
.k-chart__swatch--prev { border-top-style: dashed; border-top-color: var(--chart-prev); }

/* The plot and its y labels; focusable so the arrow keys walk the days. */
.k-chart__body { position: relative; display: grid; grid-template-columns: var(--chart-gutter) minmax(0, 1fr); border-radius: var(--radius-sm); outline: none; }
.k-chart__body:focus-visible { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--primary); }
.k-chart__y { position: relative; height: 180px; }
.k-chart__y span {
  position: absolute; right: var(--space-2); transform: translateY(-50%);
  font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.k-chart__plot { position: relative; min-width: 0; }
.k-chart__svg { display: block; overflow: visible; touch-action: pan-y; cursor: crosshair; }
.k-chart__grid { stroke: var(--border); stroke-width: 1; shape-rendering: crispEdges; }
.k-chart__base { stroke: var(--border-strong); stroke-width: 1; shape-rendering: crispEdges; }
.k-chart__fill-top { stop-color: var(--chart-cur); stop-opacity: 0.18; }
.k-chart__fill-bottom { stop-color: var(--chart-cur); stop-opacity: 0; }
.k-chart__line { fill: none; stroke: var(--chart-cur); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.k-chart__line--prev { stroke: var(--chart-prev); stroke-width: 1.5; stroke-dasharray: 4 4; }
.k-chart__line--today { stroke-opacity: 0.4; }
.k-chart__hair { stroke: var(--border-strong); stroke-width: 1; shape-rendering: crispEdges; }
.k-chart__dot { fill: var(--chart-cur); stroke: var(--surface); stroke-width: 2; }
.k-chart__dot--prev { fill: var(--chart-prev); }
.k-chart__dot--today { fill: var(--surface); stroke: var(--chart-cur); }

/* Dates under the plot, each centred on its day. */
.k-chart__x { position: relative; height: 1.25rem; margin-top: var(--space-1); }
.k-chart__x span {
  position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap;
  font-size: var(--text-xs); color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.k-chart__x span.k-chart__xl--start { transform: translateX(-6px); }
.k-chart__x span.k-chart__xl--end { transform: translateX(calc(-100% + 6px)); }
.k-chart__x span.is-active { color: var(--text-strong); font-weight: var(--weight-semibold); }

/* The readout: floats beside the day it describes, never over it. */
.k-chart__tip {
  position: absolute; top: 0; z-index: 2; pointer-events: none; min-width: 8.5rem;
  background: var(--popover-bg); border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
  padding: var(--space-2) var(--space-3); font-size: var(--text-xs); color: var(--text-muted); line-height: 1.5;
}
.k-chart__tip-day { color: var(--text-strong); font-weight: var(--weight-semibold); margin-bottom: 2px; white-space: nowrap; }
.k-chart__tip-row { display: flex; align-items: center; gap: var(--space-2); white-space: nowrap; font-variant-numeric: tabular-nums; }
.k-chart__tip-row b { color: var(--text-strong); font-size: var(--text-sm); font-weight: var(--weight-semibold); }
.k-chart__tip-key { flex: none; width: 12px; height: 0; border-top: 2px solid var(--chart-cur); }
.k-chart__tip-key--prev { border-top-style: dashed; border-top-color: var(--chart-prev); }
.k-chart__tip-date { color: var(--text-faint); }
.k-chart__tip-change { margin-top: 2px; color: var(--text); font-variant-numeric: tabular-nums; }
/* Phones: the same readout as a cream block under the chart, in the summary line's place. */
.k-chart__tip--inline {
  position: static; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3);
  margin-top: var(--space-3); background: var(--surface-warm); box-shadow: none; min-width: 0;
}
.k-chart__tip--inline .k-chart__tip-day { flex-basis: 100%; margin: 0; }
.k-chart__tip--inline .k-chart__tip-change { margin: 0; }

.k-chart__facts { margin: var(--space-3) 0 0; font-size: var(--text-sm); color: var(--text-muted); }

/* The same numbers as a table, for screen readers only (on a wrapper: a table ignores width: 1px). */
.k-chart__table {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Home's tiles + chart: a period switch dims them until the new numbers land. */
.k-home-stats { display: flex; flex-direction: column; gap: var(--space-4); transition: opacity var(--duration-normal) ease; }
.k-home-stats.is-loading { opacity: 0.55; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .k-home-stats { transition: none; } }

/* Period toggle */
/* Fully-rounded white track with a sliding coral thumb, not the flat
   near-white-on-near-white swap it had. The thumb is a separate element
   (.k-period__thumb) rather than colouring whichever button is pressed,
   because a background swap has no position to animate — this does.
   Math note: with 3px padding and NO gap between the two equal grid columns,
   width:calc(50% - 3px) makes the thumb exactly one column wide, and
   translateX(100%) (which moves by the thumb's OWN width) lands it flush
   under the second button — both hold for any track width, not just today's. */
.k-period {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  background: var(--surface);
  padding: 3px;
  border-radius: var(--radius-full);
  /* .k-page-header is a flex row with the greeting on one side and this on the
     other; without this, a flex item can shrink below its own content's
     width when the row is tight, which on a phone is exactly what forced
     "30 days" to wrap onto two lines. */
  flex-shrink: 0;
  /* A white pill on the cream canvas: lifted just off it, as the style guide's
     raised search box is. Light only — on the dark canvas the surface step is
     the edge, and a shadow there reads as a smudge. */
  box-shadow: var(--shadow-soft);
}
[data-theme="dark"] .k-period { box-shadow: none; }
.k-period__thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: calc(50% - 3px);
  height: calc(100% - 6px);
  background: var(--primary);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration-normal) var(--ease);
}
.k-period button {
  position: relative;
  z-index: 1;
  border: none; background: transparent; cursor: pointer;
  padding: var(--space-1) var(--space-4); border-radius: var(--radius-full);
  font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-muted);
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease);
}
.k-period button:hover { color: var(--text); }
.k-period button[aria-pressed="true"] { color: var(--on-primary); }
@media (max-width: 480px) {
  /* Smaller vertically (less padding/font), same nowrap guarantee — flex-shrink:0
     above is what actually stops the compression that caused the wrap; this
     just makes the now-protected control take up less room while it's at it. */
  .k-period button { padding: var(--space-1) var(--space-2); font-size: 11px; }
}

/* Home setup guide — compact, animated onboarding checklist */
.k-setup { background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); overflow: hidden; align-self: start; }
/* Open, it matches What's new beside it (taller on wide screens): the card
   stretches to the row and the steps share the extra height, rather than the
   card stopping short or ending in a blank band. Shut, it is just its header. */
.k-setup--open { align-self: stretch; display: flex; flex-direction: column; }
.k-setup--open .k-setup__listwrap { flex: 1 1 auto; }
.k-setup--open .k-setup__list { display: flex; flex-direction: column; }
.k-setup--open .k-setup__item { flex: 1 1 auto; }
.k-setup__head {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  width: 100%; background: none; border: none; cursor: pointer; text-align: left;
}
.k-setup__head:hover { background: var(--surface-hover); }
.k-setup__head-text { flex: 1; min-width: 0; }
.k-setup__title { font-size: var(--text-md); font-weight: var(--weight-bold); }
.k-setup__sub { font-size: 11px; color: var(--text-muted); margin-top: 1px; }
.k-setup__progress { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.k-setup__bar { width: 56px; height: 4px; background: var(--surface-sunken); border-radius: 999px; overflow: hidden; }
.k-setup__bar-fill { height: 100%; background: var(--primary); border-radius: 999px; transition: width 0.45s var(--ease); }
.k-setup__count { font-size: var(--text-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.k-setup__chevron { color: var(--text-faint); transition: transform 0.25s var(--ease); flex-shrink: 0; }
.k-setup__chevron--open { transform: rotate(180deg); }

/* animated collapse: grid-rows 0fr → 1fr smoothly handles the dynamic height */
.k-setup__listwrap { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.28s var(--ease); }
.k-setup__listwrap--open { grid-template-rows: 1fr; }
.k-setup__list { overflow: hidden; }
.k-setup__listwrap--open .k-setup__list { border-top: 1px solid var(--border); }

.k-setup__item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2half, 10px) var(--space-4); }
.k-setup__item + .k-setup__item { border-top: 1px solid var(--border); }
.k-setup__check {
  width: 18px; height: 18px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--border-strong); color: transparent;
  transition: background var(--duration-fast) var(--ease), border-color var(--duration-fast) var(--ease);
}
.k-setup__check--done { background: var(--primary); border-color: var(--primary); color: #fff; }
.k-setup__item-text { flex: 1; min-width: 0; }
.k-setup__item-title { font-size: var(--text-sm); font-weight: var(--weight-medium); }
.k-setup__item-desc { font-size: var(--text-xs); color: var(--text-muted); margin-top: 1px; }
.k-setup__item--done .k-setup__item-title {
  color: var(--text-faint); font-weight: 400;
  text-decoration: line-through; text-decoration-thickness: 1px; text-decoration-color: var(--border-strong);
}
.k-setup__cta {
  flex-shrink: 0;
  /* Every button in the list is at least this wide, so short labels line up;
     a longer label (a translation) grows its button instead of spilling out. */
  min-width: 7rem;
}
.k-setup__cta .k-btn {
  width: 100%;
  white-space: nowrap;
  border-radius: var(--radius-full);
  height: 1.75rem;
  padding: 0 var(--space-3);
  font-size: var(--text-xs);
}
@media (max-width: 480px) {
  .k-setup__cta { min-width: 5.75rem; }
  .k-setup__cta .k-btn { height: 1.5rem; font-size: 11px; padding: 0 var(--space-2); }
}

/* Home two-up row: left = setup guide / store at a glance, right = What's new.
   Two equal columns; stacks on narrow screens. */
.k-home-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); align-items: stretch; }
@media (max-width: 860px) { .k-home-row { grid-template-columns: minmax(0, 1fr); } }

/* What's new with Kodo: styled with the playbook family in brand-auth.css. */

/* Order detail — customer contact quick-actions, one row with a leading icon
   on each (WhatsApp/phone/copy) instead of text-only chips. */
.k-contact-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.k-contact-phone { font-size: var(--text-sm); color: var(--text); font-variant-numeric: tabular-nums; }
.k-contact-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-xs); font-weight: var(--weight-medium);
  padding: 4px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  text-decoration: none; cursor: pointer; transition: background var(--duration-fast) var(--ease);
}
.k-contact-btn:hover { background: var(--surface-hover); }
.k-contact-btn--wa { border-color: var(--success); color: var(--success-text); background: var(--success-bg); }
.k-contact-btn--wa:hover { background: var(--success-bg); filter: brightness(0.97); }
.k-copy-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-xs); font-weight: var(--weight-medium);
  padding: 4px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--text-muted);
  cursor: pointer; transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.k-copy-btn:hover { background: var(--surface-hover); color: var(--text); }

/* overflow is NOT hidden here on purpose — that would clip the toolbar's
   filter dropdowns whenever the table underneath is short. Only the clip
   wrapper below (table/cards/empty-state, not the toolbar) rounds down to
   the card's own corners. */
.k-orders-table-wrap { padding: 0; }
/* Scrolls sideways inside the card when the columns are wider than it. */
.k-orders-table-clip { overflow-x: auto; overflow-y: hidden; border-radius: 0 0 var(--radius-lg) var(--radius-lg); }
.k-orders-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.k-orders-table thead th {
  text-align: left;
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.k-orders-table tbody td { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border); }
.k-orders-table tbody tr { cursor: pointer; transition: background var(--duration-fast) var(--ease); }
.k-orders-table tbody tr:hover { background: var(--primary-soft); }
.k-orders-table tbody tr:last-child td { border-bottom: none; }
.k-orders-table__num { font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }

/* Line-item builder (create drawer) */
.k-line-builder {
  display: grid;
  grid-template-columns: 1fr 1fr auto auto;
  gap: var(--space-2);
  align-items: center;
  margin-bottom: var(--space-3);
}

/* Product search in the line builder: a combobox that searches the server, not a
   <select> (which only ever held the first 100 products). The list floats. */
.k-product-search { position: relative; min-width: 0; }
.k-product-search__list {
  position: absolute;
  z-index: 5;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  min-width: 16rem;
  max-height: 18rem;
  overflow-y: auto;
  padding: var(--space-1) 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
.k-product-search__opt {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  cursor: pointer;
}
.k-product-search__opt.is-active { background: var(--surface-hover); }
.k-product-search__thumb {
  flex: 0 0 2rem;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  color: var(--text-faint);
}
.k-product-search__thumb img { width: 100%; height: 100%; object-fit: cover; }
.k-product-search__text { display: flex; flex-direction: column; min-width: 0; }
.k-product-search__title { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.k-product-search__meta { color: var(--text-muted); font-size: var(--text-xs); }
.k-product-search__empty { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); color: var(--text-muted); }
.k-product-search__more {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-top: 1px solid var(--border);
  background: transparent;
  color: var(--primary);
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}
.k-product-search__more:disabled { color: var(--text-muted); cursor: default; }

/* A table in a narrow card scrolls sideways inside it, so every column stays
   reachable on a phone however long the words are. */
.k-table-scroll { max-width: 100%; overflow-x: auto; }

/* Items table (create + detail) */
.k-orders-items { width: 100%; border-collapse: collapse; font-size: var(--text-sm); margin-top: var(--space-2); }
.k-orders-items th {
  text-align: left;
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  font-size: var(--text-xs);
  padding: var(--space-2);
  border-bottom: 1px solid var(--border);
}
.k-orders-items td { padding: var(--space-2); border-bottom: 1px solid var(--border); vertical-align: top; }
.k-orders-items tr:last-child td { border-bottom: none; }

/* Totals — a row of stat blocks (subtotal/shipping/[vat]/total) rather than
   stacked label—value lines. Shared by both order drawers; nothing else uses
   .k-order-totals. */
.k-order-totals {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin: var(--space-2) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.k-order-totals__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  justify-content: flex-start;
}
.k-order-totals__label { font-size: var(--text-xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.k-order-totals__value { font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--text); }
.k-order-totals__item--grand { margin-left: auto; }
.k-order-totals__item--grand .k-order-totals__value { font-size: var(--text-lg); font-weight: var(--weight-bold); color: var(--primary); }

/* Detail blocks */
.k-order-statusrow { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); }
/* A chargeback on the order (V2-774). Informs; kodo takes no action on it. */
.k-order-alert {
  margin-bottom: var(--space-4); padding: var(--space-3) var(--space-4);
  background: var(--warning-bg); color: var(--warning-text);
  border-radius: var(--radius-md); font-size: var(--text-sm); line-height: 1.55;
}
.k-order-alert__title { display: block; font-weight: var(--weight-semibold); margin-bottom: var(--space-1); }
.k-order-alert p { margin: 0; }
.k-order-alert p + p { margin-top: var(--space-2); }
.k-order-block { padding: var(--space-4) 0; border-top: 1px solid var(--border); }
.k-order-block:first-of-type { border-top: none; padding-top: 0; }
/* KoombiyoShipPanel renders its own .k-order-block nested inside the Courier
   block, right after the Provider/Tracking fields — that fields grid isn't
   itself a .k-order-block, so :first-of-type (which only counts same-tag
   siblings, not siblings sharing this class) doesn't catch it: the divider
   above it rendered flush against the fields with no gap. */
.k-order-block .k-order-block { margin-top: var(--space-4); }
.k-order-block__label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-2);
}
.k-order-customer { display: flex; flex-direction: column; gap: 2px; font-size: var(--text-sm); color: var(--text-muted); }
.k-order-customer strong { color: var(--text); }

.k-order-actions { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* Payments */
.k-payments { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-3); }
.k-payment-rec {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-sunken);
  border-radius: var(--radius-md);
  flex-wrap: wrap;
}
.k-payment-rec__main { display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-sm); flex-wrap: wrap; }
.k-payment-rec__actions { display: flex; align-items: center; gap: var(--space-2); }
.k-payment-capture { display: flex; flex-direction: column; gap: var(--space-2); }

/* Customer pick-or-create (order drawer) */
/* No extra margin-bottom — .k-form-section's own gap already spaces every
   row consistently; this one had an extra margin stacked on top of that gap,
   which is what pushed "Or fill in..." further from the fields below it
   than every other row's spacing. */
.k-customer-search { margin-bottom: 0; }
.k-customer-results {
  margin-top: var(--space-1);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.k-customer-result {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.k-customer-result:last-child { border-bottom: none; }
.k-customer-result:hover { background: var(--surface-hover); }
.k-customer-linked {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-sunken);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
}
.k-customer-linked .k-link-sm { background: none; border: none; cursor: pointer; padding: 0; }

/* Inventory — "coming soon" banner + row action link button */
.k-coming-soon {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
}
.k-orders-table .k-link-sm { background: none; border: none; cursor: pointer; padding: 0; }

/* Search toolbar row inside the orders table's own card — sits above either
   the table or the empty state, never disappearing when a search comes up
   empty. .k-card gives the outer card padding:0, so this supplies its own. */
.k-orders-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border);
}
.k-orders-toolbar .k-search-input { flex: 1 1 auto; max-width: 22rem; }

/* Payment/channel/date filters — inline beside the search box on desktop,
   collapsed into a "Filters" popover on mobile (three more dropdowns next to
   an already-narrow search field doesn't fit a phone width). */
.k-orders-filters-inline { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.k-orders-filters-inline .k-select-wrap { width: auto; min-width: 9rem; flex: 0 0 auto; }

.k-orders-filters-toggle { position: relative; display: none; }
.k-orders-filters-panel {
  position: absolute;
  z-index: 40;
  top: calc(100% + var(--space-2));
  right: 0;
  width: 16rem;
  max-width: 85vw;
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* Status filter: horizontal tabs on desktop, a dropdown on mobile — seven
   tabs either wrap badly or force their own horizontal scroll on a phone.
   Borderless + shadow instead of the standard bordered field — it's reading
   as a chip/switcher here, not a form field. */
.k-orders-status-select { display: none; }
/* This one sits directly on the page's own tinted background, not inside a
   white card — the grey field fill only reads correctly against white, so
   it stays white here instead of the platform default. */
.k-orders-status-select .k-select { border: none; box-shadow: var(--shadow-sm); background: var(--surface); }

/* Mobile card list — the table's stand-in below 768px (same breakpoint as
   the Settings plan comparison's own desktop/mobile split). A horizontally
   scrolling table is a bad read on a phone; this is one order per card. */
.k-orders-cards { display: none; }
.k-order-card {
  background: var(--surface-warm);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.k-order-card__row { display: flex; align-items: center; gap: var(--space-2); }
.k-order-card__num { font-family: var(--font-display); font-weight: var(--weight-bold); }
.k-order-card__date { margin-left: auto; font-size: var(--text-xs); color: var(--text-muted); }
.k-order-card__customer { font-weight: var(--weight-medium); }
.k-order-card__meta { display: flex; justify-content: space-between; font-size: var(--text-sm); color: var(--text-muted); }
.k-order-card__total { font-weight: var(--weight-semibold); color: var(--text); }
.k-order-card__badges { display: flex; gap: var(--space-2); flex-wrap: wrap; }

@media (max-width: 768px) {
  .k-orders-filters-inline { display: none; }
  .k-orders-filters-toggle { display: inline-flex; }
  .k-orders-status-tabs { display: none; }
  .k-orders-status-select { display: block; margin-bottom: var(--space-4); }
  /* Only a table with a card list beside it (Orders, Customers) hides here.
     Inventory and Testimonials borrow this table's look but have no cards:
     hiding them left an empty card on phones, so they scroll sideways. */
  .k-orders-table--has-cards { display: none; }
  .k-orders-cards { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4); }
}

/* A plain icon sitting inside the field, not InputGroup's bordered affix chip
   (that's a heavier "attached box" look, meant for things like a currency
   symbol — not what "an icon beside the search box" was asking for). */
.k-search-input { position: relative; }
.k-search-input svg {
  position: absolute;
  top: 50%;
  left: var(--space-3);
  transform: translateY(-50%);
  color: var(--coral);
  pointer-events: none;
}
.k-search-input .k-input { padding-left: 2.25rem; }
/* A list search sitting on the page background: a white box lifted by a soft
   shadow, no border (the shared field grey disappears into the cream page).
   Keyboard focus still shows: the focus ring is an outline, not a shadow. */
.k-search-input--raised .k-input {
  background: var(--surface); border-color: transparent;
  box-shadow: var(--shadow-soft);
}
/* In dark a "raised" white well has no meaning — it came out card-coloured, a
   different grey from the select beside it. Same field colour as every other
   input there (Products, Customers, Inventory toolbars). */
[data-theme="dark"] .k-search-input--raised .k-input { background: var(--field-bg); box-shadow: none; }

/* The product status hero that used to live here is gone: the drawer header's
   status chip is the toggle now (.k-status-toggle), so a second control for the
   same value had nothing left to do. */

/* switch size variants */
.k-switch--lg { width: 3.25rem; height: 2rem; }
.k-switch--lg::after { width: 1.75rem; height: 1.75rem; }
.k-switch--lg:checked::after { translate: 1.25rem 0; }
.k-switch--sm { width: 2rem; height: 1.25rem; }
.k-switch--sm::after { width: 1rem; height: 1rem; }
.k-switch--sm:checked::after { translate: 0.75rem 0; }

/* quick status flip on product cards */
.k-card-live { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.k-card-live__lab { font-size: var(--text-xs); color: var(--text-muted); }
.k-card-live__lab--on { color: var(--success); font-weight: var(--weight-medium); }

/* duplicate-options rows */
.k-dup-opt { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) 0; cursor: pointer; }
.k-dup-opt + .k-dup-opt { border-top: 1px solid var(--border); }

/* ============================================================ image lightbox */
.k-lightbox {
  position: relative;
  border: 0;
  padding: 0;
  background: transparent;
  max-width: min(92vw, 60rem);
  overflow: visible;
}
.k-lightbox::backdrop { background: rgba(15, 10, 8, 0.78); }
.k-lightbox__img {
  display: block;
  max-width: min(92vw, 60rem);
  max-height: 84vh;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.k-lightbox__nav,
.k-lightbox__close {
  position: absolute;
  width: 2.5rem;
  height: 2.5rem;
  border: 0;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.92);
  color: #1a1a1a;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}
.k-lightbox__nav { top: 50%; transform: translateY(-50%); }
.k-lightbox__nav--prev { left: 0.75rem; }
.k-lightbox__nav--next { right: 0.75rem; }
.k-lightbox__close { top: 0.75rem; right: 0.75rem; }
.k-lightbox__dots {
  position: absolute;
  bottom: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 0.375rem;
}
.k-lightbox__dots .pd { width: 0.5rem; height: 0.5rem; border-radius: var(--radius-full); background: rgba(255, 255, 255, 0.55); }
.k-lightbox__dots .pd--on { background: #fff; }

/* ============================================================ dark: status colours as TEXT
   --danger / --success are SOLIDS, the same in both themes, because they also
   fill buttons and dots. As text on a dark card they sit near 3:1, so wherever a
   rule uses one as a text colour, dark takes the -text variant instead. Fills are
   untouched. Add a selector here, rather than a one-off override, when a new
   rule colours text with a status solid. */
[data-theme="dark"] :is(
  .k-copyval__said,
  .k-card-live__lab--on
) { color: var(--success-text); }
[data-theme="dark"] :is(
  .k-userpop__signout:hover,
  .k-settings__form-error,
  .k-savebar__error,
  .k-action-link--danger:hover:not(:disabled),
  .k-drawer__footer-error,
  .k-tag-chip button:hover,
  .k-option-row__remove:hover,
  .k-icon-btn.k-sizechart-table__colremove:hover
) { color: var(--danger-text); }
