/* zaki-ui 0.2.0 — tokens, fonts, components, shell. */

/* ── tokens.css ── */
/*
 * zaki-ui tokens — v0.1.0
 *
 * GENERATED by packages/zaki-ui/scripts/build-tokens.mjs. Do not edit: the
 * colours are read out of src/app/(system)/system.css, so this file and the
 * product cannot drift apart, and tokens.json is written from the same
 * object so the two outputs cannot disagree either.
 *
 * Dark is the default because the product's default palette is dark and 18
 * of its 19 accounts run it.
 */

/* ── the scale: one set, every theme ──────────────────────────────── */
:root {
  --zk-space-0: 0;
  --zk-space-2: 0.125rem;
  --zk-space-4: 0.25rem;
  --zk-space-6: 0.375rem;
  --zk-space-8: 0.5rem;
  --zk-space-10: 0.625rem;
  --zk-space-12: 0.75rem;
  --zk-space-16: 1rem;
  --zk-space-20: 1.25rem;
  --zk-space-24: 1.5rem;
  --zk-space-32: 2rem;
  --zk-radius-control: 0.375rem;
  --zk-radius-card: 0.625rem;
  --zk-radius-sheet: 1rem;
  --zk-radius-full: 9999px;
  --zk-text-12: 0.75rem;
  --zk-text-13: 0.8125rem;
  --zk-text-14: 0.875rem;
  --zk-text-16: 1rem;
  --zk-text-20: 1.25rem;
  --zk-text-24: 1.5rem;
  --zk-text-32: 2rem;
  --zk-weight-regular: 400;
  --zk-weight-medium: 500;
  --zk-weight-semibold: 600;
  --zk-weight-bold: 700;
  --zk-leading-body: 1.65;
  --zk-leading-table: 1.7;
  --zk-leading-note: 1.6;
  --zk-leading-display: 1.2;
  --zk-ease-standard: cubic-bezier(0.2, 0, 0.1, 1);
  --zk-duration-fast: 150ms;
  --zk-duration-base: 200ms;
  --zk-z-base: 10;
  --zk-z-sticky: 20;
  --zk-z-dock: 30;
  --zk-z-nav: 40;
  --zk-z-modal: 50;
  --zk-z-toast: 60;
  --zk-z-lock: 65;
  --zk-z-palette: 70;
  --zk-tap-min: 2.75rem;
  --zk-focus-width: 2px;
  --zk-focus-offset: 2px;
  --zk-control-h: 2.5rem;
  --zk-size-dialog: 32rem;
  --zk-size-toast: 24rem;
  --zk-measure: 60ch;
  --zk-font-arabic: 'IBM Plex Sans Arabic';
  --zk-font-sans: var(--zk-font-arabic), 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
  --zk-layout-mobile-nav-h: calc(44px + 0.75rem + 1px);
}

/* ── dark: الافتراضيّة — وهي لوحة «العمليّات» الغامقة، وتُطلَب بالاسم أيضاً ─────────────────────────────── */
:root, [data-theme="dark"] {
  --zk-color-surface-page: #050F1F;
  --zk-color-surface-card: #0A1A2E;
  --zk-color-surface-sunken: #0F2438;
  --zk-color-surface-raised: #16304A;
  --zk-color-border: #1C3A52;
  --zk-color-border-strong: #486276;
  --zk-color-text-heading: #E6F1F4;
  --zk-color-text: #E6F1F4;
  --zk-color-text-muted: #9FB6C2;
  --zk-color-text-faint: #7b8e9c;
  --zk-color-brand: #36B5CC;
  --zk-color-brand-on: #04182B;
  --zk-color-brand-hover: #60DADB;
  --zk-color-brand-soft: #0e2a3e;
  --zk-color-focus: #60DADB;
  --zk-color-warning: #E8973A;
  --zk-color-warning-soft: #20262f;
  --zk-color-error: #e65e52;
  --zk-color-error-soft: #202132;
  --zk-color-error-border: #55313a;
  --zk-color-success: #4FB865;
  --zk-color-success-soft: #112a34;
  --zk-color-chart-1: #36B5CC;
  --zk-color-chart-2: #2c6f8c;
  --zk-color-chart-3: #8E7CC3;
  --zk-color-chart-4: #C9A36A;
  --zk-color-chart-5: #7A93A6;
  --zk-color-chart-6: #C97A94;
  --zk-color-surface-sidebar: #0A1A2E;
  --zk-color-text-sidebar: #9FB6C2;
  --zk-color-scrollbar: #1C3A52;
  --zk-color-scrollbar-hover: #597183;
  --zk-color-scrollbar-sidebar: var(--zk-color-scrollbar);
  --zk-color-scrollbar-sidebar-hover: var(--zk-color-scrollbar-hover);
  --zk-shadow-raised: 0 1px 2px rgb(0 0 0 / 0.55);
  --zk-shadow-overlay: 0 2px 8px rgb(0 0 0 / 0.45), 0 16px 40px rgb(0 0 0 / 0.65);
  --zk-color-border-input: var(--zk-color-text-faint);
}

/* ── light: لوحة «النهار» ─────────────────────────────── */
[data-theme="light"] {
  --zk-color-surface-page: #FFFFFF;
  --zk-color-surface-card: #F4F8FA;
  --zk-color-surface-sunken: #E9F0F3;
  --zk-color-surface-raised: #DCE7EC;
  --zk-color-border: #D5E1E6;
  --zk-color-border-strong: #a9bac5;
  --zk-color-text-heading: #0C3252;
  --zk-color-text: #0C3252;
  --zk-color-text-muted: #4A6272;
  --zk-color-text-faint: #5f6c75;
  --zk-color-brand: #1A6382;
  --zk-color-brand-on: #FFFFFF;
  --zk-color-brand-hover: #176079;
  --zk-color-brand-soft: #e9f1f4;
  --zk-color-focus: #1A6382;
  --zk-color-warning: #9A5B12;
  --zk-color-warning-soft: #f0f0ee;
  --zk-color-error: #B42318;
  --zk-color-error-soft: #f1edef;
  --zk-color-error-border: #deb0ad;
  --zk-color-success: #1E7A3A;
  --zk-color-success-soft: #e9f2f0;
  --zk-color-chart-1: #2e9aad;
  --zk-color-chart-2: #1A6382;
  --zk-color-chart-3: #8E7CC3;
  --zk-color-chart-4: #a48456;
  --zk-color-chart-5: #758d9f;
  --zk-color-chart-6: #c1758e;
  --zk-color-surface-sidebar: #F4F8FA;
  --zk-color-text-sidebar: #4A6272;
  --zk-color-scrollbar: #D5E1E6;
  --zk-color-scrollbar-hover: #99acba;
  --zk-color-scrollbar-sidebar: var(--zk-color-scrollbar);
  --zk-color-scrollbar-sidebar-hover: var(--zk-color-scrollbar-hover);
  --zk-shadow-raised: 0 1px 2px rgb(12 50 82 / 0.06), 0 1px 3px rgb(12 50 82 / 0.08);
  --zk-shadow-overlay: 0 2px 6px rgb(12 50 82 / 0.07), 0 12px 32px rgb(12 50 82 / 0.14);
  --zk-color-border-input: var(--zk-color-text-faint);
}

/* ── calm: لوحة «الهادئة» — فاتحةٌ ثانيةٌ موجودةٌ في المنتج ─────────────────────────────── */
[data-theme="calm"] {
  --zk-color-surface-page: #F6F2EA;
  --zk-color-surface-card: #EFE9DE;
  --zk-color-surface-sunken: #E6DECF;
  --zk-color-surface-raised: #DDD3C2;
  --zk-color-border: #D8CEBC;
  --zk-color-border-strong: #abaca5;
  --zk-color-text-heading: #0C3252;
  --zk-color-text: #0C3252;
  --zk-color-text-muted: #566369;
  --zk-color-text-faint: #5d6262;
  --zk-color-brand: #1A6382;
  --zk-color-brand-on: #FFFFFF;
  --zk-color-brand-hover: #176079;
  --zk-color-brand-soft: #e4e2d9;
  --zk-color-focus: #1A6382;
  --zk-color-warning: #8e5410;
  --zk-color-warning-soft: #eae2d4;
  --zk-color-error: #B42318;
  --zk-color-error-soft: #ecdfd4;
  --zk-color-error-border: #dba69b;
  --zk-color-success: #1c7036;
  --zk-color-success-soft: #e4e3d6;
  --zk-color-chart-1: #2a8e9f;
  --zk-color-chart-2: #1A6382;
  --zk-color-chart-3: #8877bb;
  --zk-color-chart-4: #9d7f53;
  --zk-color-chart-5: #708799;
  --zk-color-chart-6: #b97088;
  --zk-color-surface-sidebar: #EFE9DE;
  --zk-color-text-sidebar: #566369;
  --zk-color-scrollbar: #D8CEBC;
  --zk-color-scrollbar-hover: #9b9f9c;
  --zk-color-scrollbar-sidebar: var(--zk-color-scrollbar);
  --zk-color-scrollbar-sidebar-hover: var(--zk-color-scrollbar-hover);
  --zk-shadow-raised: 0 1px 2px rgb(12 50 82 / 0.05), 0 1px 3px rgb(12 50 82 / 0.07);
  --zk-shadow-overlay: 0 2px 6px rgb(12 50 82 / 0.06), 0 12px 32px rgb(12 50 82 / 0.12);
  --zk-color-border-input: var(--zk-color-text-faint);
}

/*
 * RTL AND THE DIGITS.
 *
 * The product sets both on the document, not in a component: «الأرقام
 * الغربيّة» is enforced by a repo guard, not by CSS, so a package that only
 * ships colours cannot promise it. What CSS CAN promise is that the digits
 * line up in a column, and that is what this does — it is the single most
 * useful typographic decision in a product where every screen is numbers.
 */
.zk {
  background-color: var(--zk-color-surface-page);
  color: var(--zk-color-text);
  min-block-size: 100%;
}

[dir="rtl"], .zk {
  font-family: var(--zk-font-sans);
  line-height: var(--zk-leading-body);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Arabic joins its letters: spacing them apart severs the joins. */
[lang="ar"] {
  letter-spacing: 0;
  text-transform: none;
}


/* ── fonts.css ── */
/*
 * zaki-ui fonts — IBM Plex Sans Arabic, hosted here.
 *
 * GENERATED by packages/zaki-ui/scripts/build-fonts.py. Every file was
 * identified by reading its own tables, and each `unicode-range` below is
 * the set of codepoints that file ACTUALLY carries — not a block copied
 * from a website. A wrong range is the worst font bug there is: the browser
 * fetches the file, refuses to use it, and the text falls back silently.
 *
 * `font-display: swap` matches what the product does today: text is
 * readable immediately in the fallback and reflows once, rather than
 * leaving a warehouse screen blank while a font downloads.
 *
 * Licence, read out of the binaries: http://scripts.sil.org/OFL
 */

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-400-arabic.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020, U+00A0, U+0600-0604, U+0606-061B, U+061E-06A1, U+06A3-06FF, U+0750-077F, U+08A0, U+08A2-08AC, U+08B3, U+08E4-08FE, U+2010, U+FB51, U+FB53-FB55, U+FB57-FB59, U+FB5B-FB5D, U+FB5F-FB61, U+FB63-FB65, U+FB67-FB69, U+FB6B-FB6D, U+FB6F-FB71, U+FB73-FB75, U+FB77-FB79, U+FB7B-FB7D, U+FB7F-FB81, U+FB83, U+FB85, U+FB87, U+FB89, U+FB8B, U+FB8D, U+FB8F-FB91, U+FB93-FB95, U+FB97-FB99, U+FB9B-FB9D, U+FB9F, U+FBA1, U+FBA5, U+FBA7-FBA9, U+FBAB-FBAD, U+FBAF, U+FBB1-FBC1, U+FBD4-FBD6, U+FBD8, U+FBDA, U+FBDC, U+FBDF, U+FBE1, U+FBE3, U+FBE5-FBE9, U+FBFD-FBFF, U+FC03-FC04, U+FC09-FC0A, U+FC0F-FC10, U+FC13-FC14, U+FC31-FC32, U+FC35-FC36, U+FC3D-FC3E, U+FC42-FC44, U+FC4F-FC50, U+FC59-FC5D, U+FC64-FC65, U+FC67-FC6B, U+FC6D-FC71, U+FC73-FC77, U+FC79-FC7B, U+FC83-FC84, U+FC8A-FC8B, U+FC8D-FC92, U+FC94-FC96, U+FCCC, U+FCD9, U+FCF2-FCF4, U+FCFB-FCFE, U+FD05-FD08, U+FD0D-FD10, U+FD17-FD1A, U+FD21-FD24, U+FD29-FD2C, U+FD3C-FD3F, U+FDF1-FDF2, U+FDF4, U+FDFA-FDFD, U+FE70-FE74, U+FE76-FEFC;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-400-latin.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2039-203A, U+2044, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-500-arabic.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020, U+00A0, U+0600-0604, U+0606-061B, U+061E-06A1, U+06A3-06FF, U+0750-077F, U+08A0, U+08A2-08AC, U+08B3, U+08E4-08FE, U+2010, U+FB51, U+FB53-FB55, U+FB57-FB59, U+FB5B-FB5D, U+FB5F-FB61, U+FB63-FB65, U+FB67-FB69, U+FB6B-FB6D, U+FB6F-FB71, U+FB73-FB75, U+FB77-FB79, U+FB7B-FB7D, U+FB7F-FB81, U+FB83, U+FB85, U+FB87, U+FB89, U+FB8B, U+FB8D, U+FB8F-FB91, U+FB93-FB95, U+FB97-FB99, U+FB9B-FB9D, U+FB9F, U+FBA1, U+FBA5, U+FBA7-FBA9, U+FBAB-FBAD, U+FBAF, U+FBB1-FBC1, U+FBD4-FBD6, U+FBD8, U+FBDA, U+FBDC, U+FBDF, U+FBE1, U+FBE3, U+FBE5-FBE9, U+FBFD-FBFF, U+FC03-FC04, U+FC09-FC0A, U+FC0F-FC10, U+FC13-FC14, U+FC31-FC32, U+FC35-FC36, U+FC3D-FC3E, U+FC42-FC44, U+FC4F-FC50, U+FC59-FC5D, U+FC64-FC65, U+FC67-FC6B, U+FC6D-FC71, U+FC73-FC77, U+FC79-FC7B, U+FC83-FC84, U+FC8A-FC8B, U+FC8D-FC92, U+FC94-FC96, U+FCCC, U+FCD9, U+FCF2-FCF4, U+FCFB-FCFE, U+FD05-FD08, U+FD0D-FD10, U+FD17-FD1A, U+FD21-FD24, U+FD29-FD2C, U+FD3C-FD3F, U+FDF1-FDF2, U+FDF4, U+FDFA-FDFD, U+FE70-FE74, U+FE76-FEFC;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-500-latin.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2039-203A, U+2044, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-600-arabic.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020, U+00A0, U+0600-0604, U+0606-061B, U+061E-06A1, U+06A3-06FF, U+0750-077F, U+08A0, U+08A2-08AC, U+08B3, U+08E4-08FE, U+2010, U+FB51, U+FB53-FB55, U+FB57-FB59, U+FB5B-FB5D, U+FB5F-FB61, U+FB63-FB65, U+FB67-FB69, U+FB6B-FB6D, U+FB6F-FB71, U+FB73-FB75, U+FB77-FB79, U+FB7B-FB7D, U+FB7F-FB81, U+FB83, U+FB85, U+FB87, U+FB89, U+FB8B, U+FB8D, U+FB8F-FB91, U+FB93-FB95, U+FB97-FB99, U+FB9B-FB9D, U+FB9F, U+FBA1, U+FBA5, U+FBA7-FBA9, U+FBAB-FBAD, U+FBAF, U+FBB1-FBC1, U+FBD4-FBD6, U+FBD8, U+FBDA, U+FBDC, U+FBDF, U+FBE1, U+FBE3, U+FBE5-FBE9, U+FBFD-FBFF, U+FC03-FC04, U+FC09-FC0A, U+FC0F-FC10, U+FC13-FC14, U+FC31-FC32, U+FC35-FC36, U+FC3D-FC3E, U+FC42-FC44, U+FC4F-FC50, U+FC59-FC5D, U+FC64-FC65, U+FC67-FC6B, U+FC6D-FC71, U+FC73-FC77, U+FC79-FC7B, U+FC83-FC84, U+FC8A-FC8B, U+FC8D-FC92, U+FC94-FC96, U+FCCC, U+FCD9, U+FCF2-FCF4, U+FCFB-FCFE, U+FD05-FD08, U+FD0D-FD10, U+FD17-FD1A, U+FD21-FD24, U+FD29-FD2C, U+FD3C-FD3F, U+FDF1-FDF2, U+FDF4, U+FDFA-FDFD, U+FE70-FE74, U+FE76-FEFC;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-600-latin.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2039-203A, U+2044, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-700-arabic.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020, U+00A0, U+0600-0604, U+0606-061B, U+061E-06A1, U+06A3-06FF, U+0750-077F, U+08A0, U+08A2-08AC, U+08B3, U+08E4-08FE, U+2010, U+FB51, U+FB53-FB55, U+FB57-FB59, U+FB5B-FB5D, U+FB5F-FB61, U+FB63-FB65, U+FB67-FB69, U+FB6B-FB6D, U+FB6F-FB71, U+FB73-FB75, U+FB77-FB79, U+FB7B-FB7D, U+FB7F-FB81, U+FB83, U+FB85, U+FB87, U+FB89, U+FB8B, U+FB8D, U+FB8F-FB91, U+FB93-FB95, U+FB97-FB99, U+FB9B-FB9D, U+FB9F, U+FBA1, U+FBA5, U+FBA7-FBA9, U+FBAB-FBAD, U+FBAF, U+FBB1-FBC1, U+FBD4-FBD6, U+FBD8, U+FBDA, U+FBDC, U+FBDF, U+FBE1, U+FBE3, U+FBE5-FBE9, U+FBFD-FBFF, U+FC03-FC04, U+FC09-FC0A, U+FC0F-FC10, U+FC13-FC14, U+FC31-FC32, U+FC35-FC36, U+FC3D-FC3E, U+FC42-FC44, U+FC4F-FC50, U+FC59-FC5D, U+FC64-FC65, U+FC67-FC6B, U+FC6D-FC71, U+FC73-FC77, U+FC79-FC7B, U+FC83-FC84, U+FC8A-FC8B, U+FC8D-FC92, U+FC94-FC96, U+FCCC, U+FCD9, U+FCF2-FCF4, U+FCFB-FCFE, U+FD05-FD08, U+FD0D-FD10, U+FD17-FD1A, U+FD21-FD24, U+FD29-FD2C, U+FD3C-FD3F, U+FDF1-FDF2, U+FDF4, U+FDFA-FDFD, U+FE70-FE74, U+FE76-FEFC;
}

@font-face {
  font-family: 'IBM Plex Sans Arabic';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('./fonts/ibm-plex-sans-arabic-700-latin.woff2') format('woff2');
  unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308, U+2013-2014, U+2018-201A, U+201C-201E, U+2022, U+2026, U+2039-203A, U+2044, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}


/* ── components.css ── */
/*
 * zaki-ui components — seven of them, as CSS with semantic classes.
 *
 * These are the seven the discovery table found carrying NO import from the
 * app or the framework, which is why they are first: their behaviour is
 * already independent of React, so what is left is to say it in CSS and a
 * little plain JavaScript.
 *
 * ── EVERY VALUE COMES FROM A TOKEN ──
 *
 * Not one colour, spacing step, radius, size or duration is written here.
 * A test reads this file and fails on a hex, an `rgb(`, or a bare `px`
 * outside a border width. The point is not tidiness: the studio and
 * operations must be able to drift apart in a hundred ways and never in
 * their palette.
 *
 * ── RTL IS THE DEFAULT, SO THE PROPERTIES ARE LOGICAL ──
 *
 * `margin-inline-start`, `padding-inline`, `inset-inline`. Physical
 * `left`/`right` would need a second stylesheet for the other direction,
 * and the second one is always the one that goes stale.
 *
 * Load order: tokens.css, then fonts.css, then this.
 */

/* ═══ shared ═══════════════════════════════════════════════════════════ */

/*
 * BORDER-BOX, OR `inline-size: 100%` MEANS «100% PLUS THE PADDING».
 *
 * Operations never had to say this: Tailwind's preflight sets it for the
 * whole page. A package that ships no reset inherits the browser default
 * instead, and every control with a width of 100% and any padding grows
 * wider than the box it is in.
 *
 * Found by measuring the rendered demo, not by reading: a textarea sat ten
 * pixels past the inline edge of its column. One element overhanging is
 * how a page comes to scroll sideways at 360px.
 *
 * Scoped under `.zk` so the package cannot change the box model of a
 * consumer's own page — the studio has its own CSS and did not ask for a
 * reset.
 */
.zk,
.zk *,
.zk *::before,
.zk *::after {
  box-sizing: border-box;
}


.zk-icon {
  width: var(--zk-space-16);
  height: var(--zk-space-16);
  flex: none;
  fill: currentColor;
}

/*
 * THE FOCUS RING IS THE PRODUCT'S, AND IT IS `:focus-visible` ONLY.
 *
 * A ring on every mouse click reads as an error — the person did nothing
 * wrong, they clicked a button. `:focus-visible` is the browser's own
 * judgement of «this person is using a keyboard», and it is better at it
 * than any rule written here.
 */
.zk :where(button, a, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--zk-focus-width) solid var(--zk-color-focus);
  outline-offset: var(--zk-focus-offset);
}

/* ═══ field — input, select, textarea ══════════════════════════════════ */

.zk-field {
  display: block;
  inline-size: 100%;
  min-inline-size: 0;
}

.zk-field__label {
  display: block;
  margin-block-end: var(--zk-space-6);
  font-size: var(--zk-text-12);
  font-weight: var(--zk-weight-medium);
  color: var(--zk-color-text-heading);
}

.zk-input,
.zk-select,
.zk-textarea {
  inline-size: 100%;
  min-inline-size: 0;
  /*
   * 44px under a finger, 40px under a mouse — and STATED, not left to
   * padding. The product's own note: padding alone made the height a
   * by-product of the font size, and a row of controls came out four
   * heights.
   */
  block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-12);
  font: inherit;
  font-size: var(--zk-text-14);
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border-input);
  border-radius: var(--zk-radius-card);
  transition: border-color var(--zk-duration-base) var(--zk-ease-standard),
    box-shadow var(--zk-duration-base) var(--zk-ease-standard);
}

.zk-textarea {
  block-size: auto;
  min-block-size: calc(var(--zk-tap-min) * 2);
  padding-block: var(--zk-space-8);
  line-height: var(--zk-leading-body);
  resize: vertical;
}

@media (pointer: fine) {
  .zk-input,
  .zk-select {
    block-size: var(--zk-control-h);
  }
}

.zk-input::placeholder,
.zk-textarea::placeholder {
  color: var(--zk-color-text-faint);
}

.zk-input:hover:not(:disabled),
.zk-select:hover:not(:disabled),
.zk-textarea:hover:not(:disabled) {
  border-color: var(--zk-color-border-strong);
}

.zk-input:focus,
.zk-select:focus,
.zk-textarea:focus {
  outline: none;
  border-color: var(--zk-color-brand);
  box-shadow: 0 0 0 var(--zk-focus-width) var(--zk-color-brand-soft);
}

.zk-input:disabled,
.zk-select:disabled,
.zk-textarea:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/*
 * THE ERROR STATE IS NOT THE COLOUR.
 *
 * A red border alone is invisible to a reader who cannot tell red from
 * grey, so the state is `aria-invalid` — which the border follows — and
 * the sentence under it is what actually says what is wrong.
 */
.zk-input[aria-invalid='true'],
.zk-select[aria-invalid='true'],
.zk-textarea[aria-invalid='true'] {
  border-color: var(--zk-color-error);
}

.zk-input[aria-invalid='true']:focus,
.zk-select[aria-invalid='true']:focus,
.zk-textarea[aria-invalid='true']:focus {
  border-color: var(--zk-color-error);
  box-shadow: 0 0 0 var(--zk-focus-width) var(--zk-color-error-soft);
}

.zk-field__error,
.zk-field__hint {
  margin-block-start: var(--zk-space-4);
  font-size: var(--zk-text-12);
  line-height: var(--zk-leading-note);
}

.zk-field__error {
  color: var(--zk-color-error);
}

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

/* ═══ tabs ═════════════════════════════════════════════════════════════ */

/*
 * IT SCROLLS RATHER THAN WRAPS. A strip that wraps to two rows moves the
 * content under it every time a count changes.
 */
.zk-tabs {
  display: flex;
  gap: var(--zk-space-4);
  overflow-x: auto;
  border-block-end: 1px solid var(--zk-color-border);
}

.zk-tab {
  flex: none;
  white-space: nowrap;
  padding-inline: var(--zk-space-12);
  padding-block: var(--zk-space-10);
  font: inherit;
  font-size: var(--zk-text-14);
  color: var(--zk-color-text-muted);
  background: none;
  border: 0;
  border-block-end: var(--zk-focus-width) solid transparent;
  cursor: pointer;
  transition: color var(--zk-duration-base) var(--zk-ease-standard),
    border-color var(--zk-duration-base) var(--zk-ease-standard);
}

.zk-tab:hover {
  color: var(--zk-color-text-heading);
}

.zk-tab[aria-selected='true'] {
  color: var(--zk-color-brand);
  border-block-end-color: var(--zk-color-brand);
  font-weight: var(--zk-weight-semibold);
}

/*
 * A COUNT, NEVER A DOT. A count answers «is it worth opening»; a dot only
 * says «something».
 */
.zk-tab__count {
  margin-inline-start: var(--zk-space-6);
  font-size: var(--zk-text-12);
  font-variant-numeric: tabular-nums;
  color: var(--zk-color-text-faint);
}

/* ═══ rows — a table on a desk, cards in a hand ════════════════════════ */

.zk-rows {
  inline-size: 100%;
  border-collapse: collapse;
  font-size: var(--zk-text-14);
}

.zk-rows th {
  padding: var(--zk-space-10) var(--zk-space-12);
  text-align: start;
  font-size: var(--zk-text-12);
  font-weight: var(--zk-weight-semibold);
  color: var(--zk-color-text-muted);
  background-color: var(--zk-color-surface-sunken);
  border-block-end: 1px solid var(--zk-color-border);
}

.zk-rows td {
  padding: var(--zk-space-10) var(--zk-space-12);
  color: var(--zk-color-text);
  border-block-end: 1px solid var(--zk-color-border);
  line-height: var(--zk-leading-table);
}

.zk-rows tbody tr:hover td {
  background-color: var(--zk-color-surface-sunken);
}

/* A row that needs looking at, marked by more than colour: it carries a
   bar on the inline edge as well, for a reader who cannot see the tint. */
.zk-rows tr[data-alert='true'] td {
  background-color: var(--zk-color-error-soft);
}

.zk-rows tr[data-alert='true'] td:first-child {
  box-shadow: inset var(--zk-focus-width) 0 0 0 var(--zk-color-error);
}

.zk-rows__numeric {
  text-align: end;
  font-variant-numeric: tabular-nums;
}

/*
 * ON A PHONE A TABLE BECOMES A LIST OF CARDS.
 *
 * Not a horizontally scrolling table: a warehouse reads this one-handed,
 * and a column you have to swipe to is a column nobody reads. Each cell
 * grows its own label from `data-label`, so the markup is one table and
 * the reader gets whichever shape fits.
 */
@media (max-width: 640px) {
  .zk-rows thead {
    display: none;
  }

  .zk-rows tbody tr {
    display: block;
    margin-block-end: var(--zk-space-8);
    padding: var(--zk-space-8);
    background-color: var(--zk-color-surface-card);
    border: 1px solid var(--zk-color-border);
    border-radius: var(--zk-radius-card);
  }

  .zk-rows td {
    display: flex;
    justify-content: space-between;
    gap: var(--zk-space-8);
    padding: var(--zk-space-4) 0;
    border: 0;
  }

  .zk-rows td::before {
    content: attr(data-label);
    color: var(--zk-color-text-muted);
    font-size: var(--zk-text-12);
  }

  /* A column the desk needs and a hand does not. */
  .zk-rows [data-desk-only='true'] {
    display: none;
  }
}

/* ═══ modal ════════════════════════════════════════════════════════════ */

.zk-modal {
  position: fixed;
  inset: 0;
  z-index: var(--zk-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--zk-space-16);
  background-color: color-mix(in srgb, var(--zk-color-surface-page) 60%, transparent);
}

.zk-modal[hidden] {
  display: none;
}

.zk-modal__panel {
  inline-size: 100%;
  max-inline-size: var(--zk-size-dialog);
  max-block-size: 90vh;
  display: flex;
  flex-direction: column;
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-sheet);
  box-shadow: var(--zk-shadow-overlay);
}

.zk-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--zk-space-8);
  padding: var(--zk-space-16);
  border-block-end: 1px solid var(--zk-color-border);
}

.zk-modal__title {
  margin: 0;
  font-size: var(--zk-text-16);
  font-weight: var(--zk-weight-semibold);
  color: var(--zk-color-text-heading);
}

.zk-modal__body {
  padding: var(--zk-space-16);
  overflow-y: auto;
}

.zk-modal__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--zk-tap-min);
  block-size: var(--zk-tap-min);
  color: var(--zk-color-text-muted);
  background: none;
  border: 0;
  border-radius: var(--zk-radius-control);
  cursor: pointer;
}

.zk-modal__close:hover {
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-sunken);
}

/* ═══ toast ════════════════════════════════════════════════════════════ */

.zk-toasts {
  position: fixed;
  inset-block-end: var(--zk-space-16);
  inset-inline-start: var(--zk-space-16);
  z-index: var(--zk-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--zk-space-8);
  pointer-events: none;
}

.zk-toast {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
  max-inline-size: var(--zk-size-toast);
  padding: var(--zk-space-10) var(--zk-space-12);
  font-size: var(--zk-text-14);
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-card);
  box-shadow: var(--zk-shadow-overlay);
  pointer-events: auto;
}

.zk-toast[data-tone='success'] {
  border-color: var(--zk-color-success);
  color: var(--zk-color-success);
  background-color: var(--zk-color-success-soft);
}

.zk-toast[data-tone='error'] {
  border-color: var(--zk-color-error-border);
  color: var(--zk-color-error);
  background-color: var(--zk-color-error-soft);
}

.zk-toast[data-tone='warning'] {
  border-color: var(--zk-color-warning);
  color: var(--zk-color-warning);
  background-color: var(--zk-color-warning-soft);
}

/* ═══ skeleton ═════════════════════════════════════════════════════════ */

.zk-skeleton {
  display: block;
  block-size: var(--zk-space-12);
  background-color: var(--zk-color-surface-raised);
  border-radius: var(--zk-radius-control);
  animation: zk-pulse calc(var(--zk-duration-base) * 8) var(--zk-ease-standard) infinite;
}

@keyframes zk-pulse {
  50% {
    opacity: 0.5;
  }
}

/*
 * WITHOUT THE ANIMATION IT IS STILL A SKELETON.
 *
 * What the movement adds is «this is loading»; what the block does is hold
 * the space so the page does not jump when the answer arrives. The second
 * is the half that matters, and it survives.
 */
@media (prefers-reduced-motion: reduce) {
  .zk-skeleton {
    animation: none;
  }
}

/* ═══ page header ══════════════════════════════════════════════════════ */

.zk-page-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--zk-space-8);
  margin-block-end: var(--zk-space-16);
}

.zk-page-header__title {
  margin: 0;
  font-size: var(--zk-text-20);
  font-weight: var(--zk-weight-semibold);
  color: var(--zk-color-text-heading);
}

/*
 * A HEADER'S DESCRIPTION IS TEXT, NOT MARKUP.
 *
 * Two screens in the product printed `<code>` and `<strong>` at a seller
 * because a description was typed as a string containing tags and rendered
 * as one. There is nothing to enforce that here, so it is said: put words
 * in it.
 */
.zk-page-header__description {
  margin: var(--zk-space-4) 0 0;
  max-inline-size: var(--zk-measure);
  font-size: var(--zk-text-13);
  line-height: var(--zk-leading-note);
  color: var(--zk-color-text-muted);
}

/* ═══ button ═══════════════════════════════════════════════════════════ */

.zk-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--zk-space-6);
  min-block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-16);
  font: inherit;
  font-size: var(--zk-text-14);
  font-weight: var(--zk-weight-medium);
  border: 1px solid transparent;
  border-radius: var(--zk-radius-card);
  cursor: pointer;
  transition: background-color var(--zk-duration-base) var(--zk-ease-standard),
    border-color var(--zk-duration-base) var(--zk-ease-standard),
    color var(--zk-duration-base) var(--zk-ease-standard);
}

@media (pointer: fine) {
  .zk-btn {
    min-block-size: var(--zk-control-h);
  }
}

.zk-btn--primary {
  color: var(--zk-color-brand-on);
  background-color: var(--zk-color-brand);
}

.zk-btn--primary:hover:not(:disabled) {
  background-color: var(--zk-color-brand-hover);
}

.zk-btn--secondary {
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-card);
  border-color: var(--zk-color-border-input);
}

.zk-btn--secondary:hover:not(:disabled) {
  border-color: var(--zk-color-brand);
  color: var(--zk-color-brand);
}

.zk-btn--ghost {
  color: var(--zk-color-text-muted);
  background: none;
}

.zk-btn--ghost:hover:not(:disabled) {
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-sunken);
}

.zk-btn--danger {
  color: var(--zk-color-error);
  background-color: var(--zk-color-error-soft);
  border-color: var(--zk-color-error-border);
}

.zk-btn--danger:hover:not(:disabled) {
  border-color: var(--zk-color-error);
}

.zk-btn:active:not(:disabled) {
  /* A press is felt, not announced: the surface dips rather than jumps. */
  transform: translateY(1px);
}

.zk-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/*
 * LOADING IS A STATE, NOT A REPLACEMENT.
 *
 * The label stays — a button that becomes a spinner has thrown away the
 * only thing that said what it was doing — and `aria-busy` is what a
 * screen reader hears. The button is disabled while busy too, so a second
 * press cannot send a second request.
 */
.zk-btn[aria-busy="true"] {
  cursor: progress;
}

.zk-btn[aria-busy="true"] .zk-loader {
  animation: zk-spin calc(var(--zk-duration-base) * 5) linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
  .zk-btn[aria-busy="true"] .zk-loader {
    animation-duration: calc(var(--zk-duration-base) * 20);
  }

  .zk-btn:active:not(:disabled) {
    transform: none;
  }
}

/* ═══ card ═════════════════════════════════════════════════════════════ */

.zk-card {
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-card);
  box-shadow: var(--zk-shadow-raised);
}

.zk-card__body {
  padding: var(--zk-space-16);
}

.zk-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--zk-space-8);
  padding: var(--zk-space-12) var(--zk-space-16);
  border-block-end: 1px solid var(--zk-color-border);
}

/* ═══ badge · status chip · health chip ════════════════════════════════ */

.zk-badge,
.zk-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--zk-space-6);
  padding: var(--zk-space-4) var(--zk-space-10);
  font-size: var(--zk-text-12);
  font-weight: var(--zk-weight-semibold);
  border: 1px solid transparent;
  border-radius: var(--zk-radius-full);
  white-space: nowrap;
}

.zk-badge {
  color: var(--zk-color-text-muted);
  background-color: var(--zk-color-surface-sunken);
  border-color: var(--zk-color-border);
}

/*
 * A TONE IS NEVER THE ONLY SIGNAL.
 *
 * Each chip carries a word, and the colour agrees with the word rather
 * than replacing it. A reader who cannot tell the green from the amber
 * still reads «جيّد» and «مقبول».
 */
.zk-chip[data-tone="good"] {
  color: var(--zk-color-success);
  background-color: var(--zk-color-success-soft);
  border-color: var(--zk-color-success);
}

.zk-chip[data-tone="ok"] {
  color: var(--zk-color-warning);
  background-color: var(--zk-color-warning-soft);
  border-color: var(--zk-color-warning);
}

.zk-chip[data-tone="bad"] {
  color: var(--zk-color-error);
  background-color: var(--zk-color-error-soft);
  border-color: var(--zk-color-error-border);
}

.zk-chip[data-tone="unknown"] {
  color: var(--zk-color-text-muted);
  background-color: var(--zk-color-surface-sunken);
  border-color: var(--zk-color-border);
}

.zk-chip__why {
  margin-inline-start: var(--zk-space-6);
  font-weight: var(--zk-weight-regular);
  color: var(--zk-color-text-muted);
}

/* ═══ empty state ══════════════════════════════════════════════════════ */

/*
 * AN EMPTY LIST SAYS WHY IT IS EMPTY. «لا يوجد» is the answer that sends
 * somebody to ask a colleague; the reason underneath is the component.
 */
.zk-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--zk-space-6);
  padding: var(--zk-space-24) var(--zk-space-16);
  text-align: center;
  color: var(--zk-color-text-muted);
}

.zk-empty__title {
  font-size: var(--zk-text-14);
  font-weight: var(--zk-weight-semibold);
  color: var(--zk-color-text-heading);
}

.zk-empty__why {
  max-inline-size: var(--zk-measure);
  font-size: var(--zk-text-13);
  line-height: var(--zk-leading-note);
}

/* ═══ money ════════════════════════════════════════════════════════════ */

/*
 * TABULAR FIGURES. A column of numbers with proportional digits is ragged
 * — a 1 is narrower than a 7 — so totals do not line up and the eye cannot
 * scan down. On a screen that is entirely numbers this is the single most
 * useful typographic decision there is.
 *
 * The digits themselves are the caller's business: CSS cannot force 0-9,
 * and a rule that pretended to would be worse than none.
 */
.zk-money {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  white-space: nowrap;
  direction: ltr;
  unicode-bidi: isolate;
}

.zk-money[data-tone="in"] {
  color: var(--zk-color-success);
}

.zk-money[data-tone="out"] {
  color: var(--zk-color-error);
}

.zk-money__currency {
  margin-inline-start: var(--zk-space-4);
  font-size: var(--zk-text-12);
  color: var(--zk-color-text-muted);
}

/* ═══ progress and task ════════════════════════════════════════════════ */

.zk-progress {
  inline-size: 100%;
  block-size: var(--zk-space-6);
  background-color: var(--zk-color-surface-raised);
  border-radius: var(--zk-radius-full);
  overflow: hidden;
}

.zk-progress__bar {
  block-size: 100%;
  inline-size: 0;
  background-color: var(--zk-color-brand);
  border-radius: var(--zk-radius-full);
  transition: inline-size var(--zk-duration-base) var(--zk-ease-standard);
}

/*
 * A BAR WITH NO NUMBER IS A BAR NOBODY TRUSTS. «جارٍ» for four minutes and
 * «3 من 9» for four minutes are different experiences.
 */
.zk-task {
  display: flex;
  flex-direction: column;
  gap: var(--zk-space-6);
  padding: var(--zk-space-10) var(--zk-space-12);
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-card);
}

.zk-task__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--zk-space-8);
  font-size: var(--zk-text-13);
}

.zk-task__count {
  font-variant-numeric: tabular-nums;
  color: var(--zk-color-text-muted);
}

/* ═══ tooltip ══════════════════════════════════════════════════════════ */

/*
 * A HINT, NEVER THE ONLY PLACE SOMETHING IS SAID. It does not appear on a
 * touch device — there is no hover there — and a tooltip that needs a long
 * press is a secret. Anything a person must know belongs in the page.
 */
.zk-tooltip {
  position: relative;
  display: inline-flex;
}

.zk-tooltip__bubble {
  position: absolute;
  inset-block-end: calc(100% + var(--zk-space-6));
  inset-inline-start: 50%;
  transform: translateX(50%);
  z-index: var(--zk-z-toast);
  max-inline-size: var(--zk-size-toast);
  padding: var(--zk-space-6) var(--zk-space-10);
  font-size: var(--zk-text-12);
  line-height: var(--zk-leading-note);
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-raised);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-control);
  box-shadow: var(--zk-shadow-overlay);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--zk-duration-fast) var(--zk-ease-standard);
}

.zk-tooltip:hover .zk-tooltip__bubble,
.zk-tooltip:focus-within .zk-tooltip__bubble {
  opacity: 1;
}

/* ═══ dropdown ═════════════════════════════════════════════════════════ */

.zk-dropdown {
  position: relative;
  display: inline-block;
}

.zk-dropdown__menu {
  position: absolute;
  inset-block-start: calc(100% + var(--zk-space-4));
  inset-inline-start: 0;
  z-index: var(--zk-z-dock);
  min-inline-size: var(--zk-size-toast);
  max-block-size: calc(var(--zk-tap-min) * 6);
  overflow-y: auto;
  padding: var(--zk-space-4);
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-card);
  box-shadow: var(--zk-shadow-overlay);
}

.zk-dropdown__menu[hidden] {
  display: none;
}

.zk-dropdown__item {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
  inline-size: 100%;
  min-block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-10);
  font: inherit;
  font-size: var(--zk-text-14);
  text-align: start;
  color: var(--zk-color-text);
  background: none;
  border: 0;
  border-radius: var(--zk-radius-control);
  cursor: pointer;
}

.zk-dropdown__item:hover,
.zk-dropdown__item[data-active="true"] {
  background-color: var(--zk-color-surface-sunken);
}

.zk-dropdown__item[aria-selected="true"] {
  color: var(--zk-color-brand);
  background-color: var(--zk-color-brand-soft);
}

/* ═══ toolbar ══════════════════════════════════════════════════════════ */

.zk-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--zk-space-8);
  padding: var(--zk-space-8);
  background-color: var(--zk-color-surface-card);
  border: 1px solid var(--zk-color-border);
  border-radius: var(--zk-radius-card);
}

.zk-toolbar__gap {
  flex: 1 1 auto;
}

/* ═══ switch and checkbox ══════════════════════════════════════════════ */

/*
 * THE TICK BOX IS TINTED, because a browser paints an untinted one in its
 * own colour — and on a dark palette that is a white square. The one
 * property that tints a native control is `accent-color`, so the native
 * control is kept and given it: a hand-built box would lose the keyboard
 * behaviour the native one has for free.
 */
.zk-checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--zk-space-8);
  min-block-size: var(--zk-tap-min);
  font-size: var(--zk-text-14);
  color: var(--zk-color-text);
  cursor: pointer;
}

.zk-checkbox input {
  inline-size: var(--zk-space-20);
  block-size: var(--zk-space-20);
  flex: none;
  accent-color: var(--zk-color-brand);
  cursor: inherit;
}

.zk-checkbox:has(input:disabled) {
  opacity: 0.5;
  cursor: not-allowed;
}

.zk-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--zk-space-8);
  min-block-size: var(--zk-tap-min);
  font-size: var(--zk-text-14);
  color: var(--zk-color-text);
  cursor: pointer;
}

.zk-switch__track {
  position: relative;
  inline-size: calc(var(--zk-space-20) * 2);
  block-size: var(--zk-space-20);
  flex: none;
  background-color: var(--zk-color-surface-raised);
  border: 1px solid var(--zk-color-border-input);
  border-radius: var(--zk-radius-full);
  transition: background-color var(--zk-duration-base) var(--zk-ease-standard);
}

.zk-switch__thumb {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  inline-size: var(--zk-space-16);
  block-size: var(--zk-space-16);
  margin: var(--zk-space-2);
  background-color: var(--zk-color-text);
  border-radius: var(--zk-radius-full);
  transition: inset-inline-start var(--zk-duration-base) var(--zk-ease-standard);
}

.zk-switch input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.zk-switch input:checked ~ .zk-switch__track {
  background-color: var(--zk-color-brand);
  border-color: var(--zk-color-brand);
}

.zk-switch input:checked ~ .zk-switch__track .zk-switch__thumb {
  inset-inline-start: var(--zk-space-20);
  background-color: var(--zk-color-brand-on);
}

.zk-switch input:focus-visible ~ .zk-switch__track {
  outline: var(--zk-focus-width) solid var(--zk-color-focus);
  outline-offset: var(--zk-focus-offset);
}

/* ═══ stepper ══════════════════════════════════════════════════════════ */

/*
 * SIX STATES, AND EACH CARRIES A COLOUR **AND** AN ICON **AND** A WORD.
 *
 * A pipeline told only in colour is unreadable to a colour-blind reader
 * and unreadable to anybody in sunlight. `awaiting_choice` and `failed`
 * are the two that stop the line, and they have to be distinguishable at a
 * glance from `running`, which does not.
 */
.zk-stepper {
  display: flex;
  flex-direction: column;
  gap: var(--zk-space-2);
}

.zk-step {
  display: flex;
  align-items: center;
  gap: var(--zk-space-10);
  min-block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-10);
  font-size: var(--zk-text-14);
  color: var(--zk-color-text);
  border-inline-start: var(--zk-focus-width) solid var(--zk-color-border);
}

.zk-step__state {
  margin-inline-start: auto;
  font-size: var(--zk-text-12);
  color: var(--zk-color-text-muted);
}

.zk-step[data-state="not_started"] {
  color: var(--zk-color-text-faint);
}

.zk-step[data-state="running"] {
  border-inline-start-color: var(--zk-color-brand);
  color: var(--zk-color-brand);
}

.zk-step[data-state="awaiting_choice"] {
  border-inline-start-color: var(--zk-color-warning);
  color: var(--zk-color-warning);
}

.zk-step[data-state="approved"] {
  border-inline-start-color: var(--zk-color-success);
  color: var(--zk-color-success);
}

.zk-step[data-state="failed"] {
  border-inline-start-color: var(--zk-color-error);
  color: var(--zk-color-error);
}

/* Ran, and its answer is older than what it was computed from. */
.zk-step[data-state="stale"] {
  border-inline-start-color: var(--zk-color-text-faint);
  color: var(--zk-color-text-muted);
}

.zk-step[data-state="running"] .zk-loader {
  animation: zk-spin calc(var(--zk-duration-base) * 5) linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .zk-step[data-state="running"] .zk-loader {
    animation: none;
  }
}


/* ── shell.css ── */
/*
 * zaki-ui shell — the frame every Zaki app sits in.
 *
 * A header across the top, a sidebar beside the content, and on a phone
 * the sidebar becomes a drawer. Extracted from the operations shell; the
 * behaviour that needs a keyboard lives in shell.js.
 *
 * NO SHADOW DOM. The custom elements here work on their light DOM, so this
 * stylesheet reaches inside them and the consumer's own CSS does too. A
 * shadow root would encapsulate the very tokens the package exists to
 * share, and every app would need a second copy of them piped in.
 *
 * Every value is a token. Logical properties throughout, because the
 * default direction is RTL and a second stylesheet for the other way round
 * is the one that goes stale.
 */

zk-app-shell {
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    'header header'
    'sidebar content';
  min-block-size: 100vh;
  background-color: var(--zk-color-surface-page);
  color: var(--zk-color-text);
  font-family: var(--zk-font-sans);
  line-height: var(--zk-leading-body);
  font-variant-numeric: tabular-nums;
}

zk-app-shell > [slot='header'] {
  grid-area: header;
}

zk-app-shell > [slot='sidebar'] {
  grid-area: sidebar;
}

zk-app-shell > [slot='content'] {
  grid-area: content;
  /*
   * `min-inline-size: 0` is the whole reason a grid child stops forcing
   * the page wider than the screen: without it a long table or an
   * unbreakable order number pushes the column out and the page scrolls
   * sideways at 360px. This one line is the 360px rule.
   */
  min-inline-size: 0;
  padding: var(--zk-space-16);
  overflow-x: hidden;
}

/* ═══ header ═══════════════════════════════════════════════════════════ */

.zk-header {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
  padding: var(--zk-space-8) var(--zk-space-12);
  background-color: var(--zk-color-surface-card);
  border-block-end: 1px solid var(--zk-color-border);
}

/* Follows the direction on its own: `start` is the right in RTL. */
.zk-header__start {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
}

.zk-header__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--zk-space-6);
  margin-inline: auto;
}

.zk-header__end {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
}

.zk-header__title {
  margin: 0;
  font-size: var(--zk-text-16);
  font-weight: var(--zk-weight-semibold);
  color: var(--zk-color-text-heading);
}

/* ═══ sidebar ══════════════════════════════════════════════════════════ */

.zk-sidebar {
  inline-size: calc(var(--zk-space-32) * 8);
  max-inline-size: 80vw;
  block-size: 100%;
  overflow-y: auto;
  padding: var(--zk-space-8);
  background-color: var(--zk-color-surface-sidebar);
  border-inline-end: 1px solid var(--zk-color-border);
}

.zk-sidebar::-webkit-scrollbar {
  inline-size: var(--zk-space-6);
}

.zk-sidebar::-webkit-scrollbar-thumb {
  background-color: var(--zk-color-scrollbar-sidebar);
  border-radius: var(--zk-radius-full);
}

.zk-sidebar::-webkit-scrollbar-thumb:hover {
  background-color: var(--zk-color-scrollbar-sidebar-hover);
}

.zk-nav-group {
  margin-block-end: var(--zk-space-4);
}

/*
 * THE GROUP HOLDING THE CURRENT PAGE OPENS ITSELF AND CANNOT BE SHUT.
 *
 * Fifty screens in one scroll means hunting for the one you want, so the
 * groups collapse — but collapsing the one you are standing in hides where
 * you are, and then the sidebar stops answering the question it exists
 * for. `data-current` disables the toggle rather than hiding it, so the
 * control is visibly inert instead of mysteriously unresponsive.
 */
.zk-nav-group__head {
  display: flex;
  align-items: center;
  gap: var(--zk-space-6);
  inline-size: 100%;
  min-block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-8);
  font: inherit;
  font-size: var(--zk-text-12);
  font-weight: var(--zk-weight-semibold);
  text-align: start;
  color: var(--zk-color-text-faint);
  background: none;
  border: 0;
  border-radius: var(--zk-radius-control);
  cursor: pointer;
}

.zk-nav-group__head:hover {
  color: var(--zk-color-text-sidebar);
}

.zk-nav-group[data-current='true'] .zk-nav-group__head {
  color: var(--zk-color-text-sidebar);
  cursor: default;
}

.zk-nav-group__caret {
  margin-inline-start: auto;
  transition: transform var(--zk-duration-base) var(--zk-ease-standard);
}

.zk-nav-group[aria-expanded='false'] .zk-nav-group__caret {
  transform: rotate(-90deg);
}

.zk-nav-group__items[hidden] {
  display: none;
}

.zk-nav-item {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
  min-block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-10);
  font-size: var(--zk-text-14);
  color: var(--zk-color-text-sidebar);
  text-decoration: none;
  border-radius: var(--zk-radius-control);
}

.zk-nav-item:hover {
  color: var(--zk-color-text);
  background-color: var(--zk-color-surface-sunken);
}

.zk-nav-item[aria-current='page'] {
  color: var(--zk-color-brand);
  background-color: var(--zk-color-brand-soft);
  font-weight: var(--zk-weight-medium);
}

/*
 * A COUNT WHEN THERE IS ONE, A DOT WHEN THERE IS ONLY «SOMETHING».
 *
 * A count answers «is it worth opening». A dot only says that it is not
 * empty, and a dot where a count was available is a number somebody threw
 * away.
 */
.zk-nav-item__count {
  margin-inline-start: auto;
  font-size: var(--zk-text-12);
  font-variant-numeric: tabular-nums;
  color: var(--zk-color-text-muted);
}

.zk-nav-item__dot {
  margin-inline-start: auto;
  inline-size: var(--zk-space-8);
  block-size: var(--zk-space-8);
  border-radius: var(--zk-radius-full);
  background-color: var(--zk-color-warning);
}

/* ═══ the drawer, on a phone ═══════════════════════════════════════════ */

.zk-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--zk-z-nav);
  background-color: color-mix(in srgb, var(--zk-color-surface-page) 60%, transparent);
}

.zk-scrim[hidden] {
  display: none;
}

@media (max-width: 768px) {
  zk-app-shell {
    grid-template-columns: 1fr;
    grid-template-areas:
      'header'
      'content';
  }

  zk-app-shell > [slot='sidebar'] {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: calc(var(--zk-z-nav) + 1);
    transform: translateX(100%);
    transition: transform var(--zk-duration-base) var(--zk-ease-standard);
  }

  /* `start` is the right edge in RTL, so the drawer slides off to the
     right there and to the left in LTR — one rule, both directions. */
  [dir='ltr'] zk-app-shell > [slot='sidebar'] {
    transform: translateX(-100%);
  }

  zk-app-shell[data-drawer='open'] > [slot='sidebar'] {
    transform: translateX(0);
  }
}

@media (min-width: 769px) {
  .zk-drawer-toggle,
  .zk-scrim {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  zk-app-shell > [slot='sidebar'] {
    transition: none;
  }
}

/* ═══ app switcher ═════════════════════════════════════════════════════ */

zk-app-switcher {
  display: block;
}

.zk-apps {
  display: flex;
  flex-direction: column;
  gap: var(--zk-space-2);
  padding: var(--zk-space-4);
}

.zk-app {
  display: flex;
  align-items: center;
  gap: var(--zk-space-8);
  min-block-size: var(--zk-tap-min);
  padding-inline: var(--zk-space-10);
  font-size: var(--zk-text-14);
  color: var(--zk-color-text);
  text-decoration: none;
  border-radius: var(--zk-radius-control);
}

.zk-app:hover {
  background-color: var(--zk-color-surface-sunken);
}

.zk-app[aria-current='true'] {
  color: var(--zk-color-brand);
  background-color: var(--zk-color-brand-soft);
}

/*
 * «UNAVAILABLE» IS THE DEFAULT, NOT THE EXCEPTION.
 *
 * The studio runs on the owner's own machine. It is off more often than it
 * is on, and an entry that looks live until it is clicked teaches people
 * the menu lies. So a link is dimmed and labelled until something answers,
 * and the word is there for a reader who cannot see the dimming.
 */
.zk-app[data-state='unavailable'] {
  color: var(--zk-color-text-faint);
  cursor: not-allowed;
}

.zk-app[data-state='checking'] {
  color: var(--zk-color-text-muted);
}

.zk-app__state {
  margin-inline-start: auto;
  font-size: var(--zk-text-12);
  color: var(--zk-color-text-faint);
}
