/* ═══════════════════════════════════════════════════════════════════════════
   MODUS AI ECOSYSTEM — Unified Design System v2.0
   ─────────────────────────────────────────────────────────────────────────
   THE single source of truth for all MODUS AI platforms.
   Load this file FIRST, before any platform-specific styles.

   CANONICAL PATH — every repo, no exceptions:
       public/css/modus-design-system.css
       served at  /css/modus-design-system.css

   Usage:
     <html data-platform="do|tools|commerce|halal|school|member|mall|campus|dgcs|esg"
           data-theme="light|dark">
     <link rel="stylesheet" href="/css/modus-design-system.css">

   Platforms — the ONLY difference between systems is the accent:
     do        M-EasyDo AI+ Super App                  #1a73e8  blue
     tools     M-EasyTools AI+ Super App               #E8622A  orange
     commerce  M-EasyCommerce AI+ Super App            #16A34A  green
     halal     M-EasyHalal AI+ Super App               #0D9488  teal
     school    M-EasySchool AI+ Super App              #1E40AF  royal blue
     member    M-EasyMember AI+ Super App              #7C3AED  purple
     mall      M-EasyMall AI+ Super App                #D97706  amber
     campus    The Campus Sport Center Super App AI+   #DC2626  crimson
     dgcs      Dragon Ginseng CS AI+                   #7C2D12  brown
     esg       Modus ESG Super App AI+                 #4D7C0F  moss green

   v2.0 changes (2026-08-05):
     + 4 missing platform accents added (member, mall, campus, dgcs).
       Before this, those 4 lived only in per-repo FORKS. Halal and School
       had the same problem and Halal was rendering in M-EasyDo's blue on
       every page except its login screen.
     + --accent-light and --accent-contrast now defined for ALL 9 platforms.
       Previously only "school" declared --accent-light, so any other
       platform using it rendered transparent and threw nothing.
     + Dark-mode accent parity for all 9.
     + §48 AUTH LAYER — login / register / forgot / OTP. The design system
       never defined an auth surface, which is exactly why every platform's
       login portal was built differently.
     + §49 SETTINGS LAYER — the settings shell every platform must use.
     + §50 responsive rules for both new layers.

   RULE: this file is edited in ONE place and copied byte-identical to all
   9 repos. A per-repo fork of this file is a defect, not a customisation.
   If a platform needs something this file does not have, it is added HERE.
═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. GOOGLE FONTS ──────────────────────────────────────────────────── */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@500;600;700;800;900&display=swap');

/* ── 2. CSS VARIABLES — LIGHT MODE DEFAULTS ──────────────────────────── */
:root {
  /* Platform accent — overridden by [data-platform] */
  --accent: #1a73e8;
  --accent-2: #1557b0;
  --accent-bg: rgba(26,115,232,0.08);
  --accent-glow: rgba(26,115,232,0.20);

  /* Page backgrounds */
  --bg: #f8f9fa;
  --bg-soft: #f0f2f5;

  /* Surface — cards, panels, inputs */
  --surface: #ffffff;
  --surface-2: #f5f7fa;

  /* Borders */
  --border: #e2e8f0;
  --border-2: #cbd5e1;

  /* Text */
  --text: #1e293b;
  --text-2: #475569;
  /* WCAG. #94a3b8 was 2.56:1 on --surface and 2.43:1 on --bg — it is the
     colour of .stat-label, .stat-sub, .text-muted, .empty-state p,
     .page-subtitle, .step-label and about thirty other rules, which is most of
     the secondary text in the product. §6 asks for 4.5:1 and nothing had ever
     checked it. Derived, not chosen: same hue, lightness walked down until the
     worst backdrop cleared 4.6. See Run 54. */
  --muted: #5a6d87;
  /* THE ACCENT USED AS TEXT, which is not the same job as the accent.
     A brand colour is chosen to be recognisable on a surface, not legible on
     one, and eight of the eleven platform accents are too light to read on
     white while ALL ELEVEN are too dark to read on a dark surface — ESG's
     #4D7C0F is 2.38:1 on --surface in dark mode.
     Defaults to --accent, so a platform that does not need an override does
     not carry one, and the six-token rule is untouched. Backgrounds, borders,
     rings and rails all keep --accent; only FOREGROUND text moves. */
  --accent-text: var(--accent);

  /* Brand — sidebar background */
  --brand: #0f172a;
  --brand-2: #1e293b;
  --brand-3: #263348;

  /* Status palette.
     THESE ARE FOREGROUNDS AS WELL AS FILLS. Every .badge-*, .alert-* and
     .tag-* pairs one of them with its own 8% tint, and at the values shipped
     until Run 54 that pairing measured between 2.8:1 and 3.9:1 — the entire
     quiet colour language of the system sat below §6's 4.5:1, in both themes,
     on every platform, and nothing had ever checked it.
     Darkened here for light mode and overridden lighter in [data-theme="dark"].
     Derived, not chosen: hue and saturation held, lightness walked until the
     worst backdrop cleared 4.6. The -bg tints are deliberately NOT moved — at
     8% the hue difference is imperceptible, and leaving them means every
     existing surface keeps exactly the colour it had. */
  /* #ef4444 until Run 54, where the derived-pairs check found .btn-danger at
     3.76:1 — white on it, in both themes, since v2.0. A fill that carries white
     text has to be dark enough to; a fill that is only a dot or a bar does not
     care, so deepening it costs nothing and fixes the one place it does. */
  --red: #c81e1e;
  --red-bg: rgba(239,68,68,0.08);
  /* Aliases — --danger is the name people reach for. Both were used in
     M-EasyHalal's modus-widget.js and resolved to nothing.             */
  --danger: var(--red);
  --danger-bg: var(--red-bg);
  --green: #16a34a;
  --green-bg: rgba(22,163,74,0.08);
  /* Same reason as --red: .btn-gold is white on --amber and measured 3.19:1. */
  --amber: #a25904;
  --amber-bg: rgba(217,119,6,0.08);
  --blue: #1a73e8;
  --blue-bg: rgba(26,115,232,0.08);
  --purple: #7c3aed;
  --purple-bg: rgba(124,58,237,0.08);
  /* THE STATUS COLOURS USED AS TEXT. Every .badge-*, .alert-* and .tag-* pairs
     one of these with its own 8% tint, and at the fill values that pairing
     measured between 2.8:1 and 3.9:1 — the entire quiet colour language of the
     system sat below §6's 4.5:1, in both themes, on every platform, and
     nothing had ever checked it.
     Derived, not chosen: hue and saturation held, lightness walked until the
     worst backdrop cleared 4.6. --purple already cleared it and is unchanged. */
  --red-text: #d41212;
  --green-text: #117b38;
  --amber-text: #a25904;
  --blue-text: #1567d3;
  --purple-text: var(--purple);

  /* Shadows */
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.07), 0 1px 2px rgba(0,0,0,0.04);
  --shadow: 0 4px 16px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
  --shadow-lg: 0 12px 40px rgba(0,0,0,0.12), 0 4px 12px rgba(0,0,0,0.06);

  /* Shape */
  --r: 12px;
  --r-sm: 8px;
  --r-lg: 18px;
  --r-xl: 24px;

  /* Layout dimensions */
  --sidebar-w: 240px;
  --topbar-h: 58px;

  /* Typography */
  /* Two fonts, two jobs. Inter for everything you READ, Outfit for
     everything you SCAN. Never introduce a third.                       */
  --font: 'Inter', system-ui, -apple-system, sans-serif;
  --font-display: 'Outfit', 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Mono', monospace;

  /* Motion and gamification — see §50.
     DECLARED HERE, IN :root, DELIBERATELY. ESG inlines a token floor that is
     extracted BY SELECTOR (src/utils/layout.js), and :root is on that list.
     A token declared under any other selector is absent from that floor, and
     the floor is what renders when the linked stylesheet fails — so it would
     break only in the situation it exists for.                            */
  --motion-fast: 120ms;
  --motion-base: 220ms;
  --motion-slow: 420ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  /* Joins the --shadow-sm / --shadow / --shadow-lg family, so it is
     --shadow-glow and not --elev-glow. A new prefix beside an established
     one is a fork in miniature.                                          */
  --shadow-glow: 0 0 0 4px var(--accent-glow), 0 8px 24px var(--accent-glow);
  /* Geometry, joining --sidebar-w / --topbar-h. Theme-neutral by nature:
     a rail is the same width in the dark.                                */
  --rail-w: 3px;
  --node-size: 28px;
  --ring-thickness: 8px;
  /* The deliberately dark "command centre" surface. Dark in BOTH themes —
     that is the point of it — so it carries a dark-mode value below rather
     than inverting.                                                      */
  --surface-deep: #0f172a;
}

/* ── 3. PLATFORM ACCENT OVERRIDES ────────────────────────────────────── */
/* Every platform declares the SAME six tokens. No platform may declare an
   accent token that the others do not.

   Adding a platform means TWO edits in this file, not one: the six-token
   block below, AND the matching [data-theme="dark"] --accent-light rule in
   §4. This comment said "one block here — nothing else in this file
   changes" until migs was added as the eleventh; a platform that took it at
   its word would ship a light tint sitting on a dark surface.             */

[data-platform="do"] {
  --accent: #1a73e8;
  --accent-2: #1557b0;
  --accent-bg: rgba(26,115,232,0.08);
  --accent-glow: rgba(26,115,232,0.20);
  --accent-light: #D6E4FA;
  --accent-text: #1461c5;
  --accent-contrast: #ffffff;
}

[data-platform="tools"] {
  --accent: #E8622A;
  --accent-2: #C44E1E;
  --accent-bg: rgba(232,98,42,0.08);
  --accent-glow: rgba(232,98,42,0.20);
  --accent-light: #FBE2D8;
  --accent-text: #b24213;
  --accent-contrast: #ffffff;
}

[data-platform="commerce"] {
  --accent: #16A34A;
  --accent-2: #0f7a37;
  --accent-bg: rgba(22,163,74,0.08);
  --accent-glow: rgba(22,163,74,0.20);
  --accent-light: #D7F0E1;
  --accent-text: #107636;
  --accent-contrast: #ffffff;
  /* Backward-compat aliases for existing inline styles in route files.
     DEPRECATED — do not use in new code. Delete once Commerce route files
     are cleaned of inline colour styles.                                  */
  --brown-dark: #3D1F0A;
  --brown-mid: #5C3317;
  --brown-light: #8B5E3C;
  --gold: #B8860B;
  --cream: var(--bg);
  --gray-50: var(--bg-soft);
  --gray-100: var(--border);
  --gray-200: var(--border);
  --gray-400: var(--muted);
  --gray-600: var(--text-2);
  --gray-800: var(--text);
  --orange: var(--amber);
}

[data-platform="halal"] {
  --accent: #0D9488;
  --accent-2: #0F766E;
  --accent-bg: rgba(13,148,136,0.08);
  --accent-glow: rgba(13,148,136,0.20);
  --accent-light: #D2EDEA;
  --accent-text: #0a736a;
  --accent-contrast: #ffffff;
}

[data-platform="school"] {
  --accent: #1E40AF;
  --accent-2: #1E3A8A;
  --accent-bg: rgba(30,64,175,0.08);
  --accent-glow: rgba(30,64,175,0.20);
  --accent-light: #DBEAFE;
  --accent-contrast: #ffffff;
}

[data-platform="member"] {
  --accent: #7C3AED;
  --accent-2: #6D28D9;
  --accent-bg: rgba(124,58,237,0.08);
  --accent-glow: rgba(124,58,237,0.20);
  --accent-light: #E9DDFD;
  --accent-text: #7631ec;
  --accent-contrast: #ffffff;
}

[data-platform="mall"] {
  --accent: #D97706;
  --accent-2: #B45309;
  --accent-bg: rgba(217,119,6,0.08);
  --accent-glow: rgba(217,119,6,0.20);
  --accent-light: #FBEBD5;
  --accent-text: #9d5604;
  --accent-contrast: #ffffff;
}

[data-platform="campus"] {
  --accent: #DC2626;
  --accent-2: #B91C1C;
  --accent-bg: rgba(220,38,38,0.08);
  --accent-glow: rgba(220,38,38,0.20);
  --accent-light: #FBDBDB;
  --accent-text: #bf1f1f;
  --accent-contrast: #ffffff;
}

[data-platform="esg"] {
  /* Modus ESG Super App AI+. Values taken from ESG's own
     public/css/modus-design-system.css:229 per §2b — the README says the
     same thing, but the README is not the source of truth. ESG chose these
     itself and shipped them before the master knew it existed.            */
  --accent: #4D7C0F;
  --accent-2: #3F6212;
  --accent-bg: rgba(77,124,15,0.08);
  --accent-glow: rgba(77,124,15,0.20);
  --accent-light: #E4EFD1;
  --accent-text: #47730e;
  --accent-contrast: #ffffff;
}

[data-platform="dgcs"] {
  /* #7C2D12, NOT #C2410C. Dragon Ginseng has shipped this brown since its
     first commit (4c19d49) and test/harness.js:187 asserts it. v2.0 briefly
     carried #C2410C because AUDIT_REPORT_CURRENT.md recorded that value and
     it was copied here without checking the code. Verify accents against
     the repo, never against a document.                                    */
  --accent: #7C2D12;
  --accent-2: #5C1F0C;
  --accent-bg: rgba(124,45,18,0.08);
  --accent-glow: rgba(124,45,18,0.20);
  --accent-light: #FEF0E7;
  --accent-contrast: #ffffff;
}

[data-platform="migs"] {
  /* MIGS CS AI+ — Mutiara International Grammar School, Ampang Jaya.
     #344889 is taken from the SCHOOL'S OWN live stylesheet, not from a
     brand document: migs.edu.my serves `a, .page-title { color: #344889 }`,
     and the same value drives ::selection and input focus borders. It is
     the interaction colour MIGS actually chose, and it matches the navy of
     the school crest.

     Two colours were deliberately NOT used. #E2312C (the crest red) appears
     only as <font color> emphasis inside headings — decoration, never an
     interaction state — and it would sit on top of Campus's #DC2626. The
     Astra theme globals (--e-global-color-astglobalcolor0: #0170B9 and
     friends) are the THEME's stock defaults shipped with every Astra site,
     so they say nothing about MIGS.

     Nearest sibling is school #1E40AF. Distinguishable, but these are the
     two education platforms — check them side by side before adding a
     third blue.                                                            */
  --accent: #344889;
  --accent-2: #29386B;
  --accent-bg: rgba(52,72,137,0.08);
  --accent-glow: rgba(52,72,137,0.20);
  --accent-light: #E1E6F4;
  --accent-contrast: #ffffff;   /* 8.6:1 on #344889 — WCAG AAA */
}

[data-platform="pos"] {
  /* M-EasyPOS AI+ — point of sale, inventory and warehouse.

     #BE185D is a deep rose. It was chosen by MEASURING the ecosystem rather
     than by taste: every existing accent was converted to HSL and the
     300–345° magenta band was the only region with nothing in it. Nearest
     neighbour by hue is campus #DC2626 at 25° away, and the two do not read
     alike — campus is a pure red, this is magenta-shifted, and at sidebar
     size that is the difference between "red" and "raspberry".

     Contrast, measured not assumed, because the ecosystem already carries
     eleven text pairs below 4.5:1 and a new platform should not add a
     twelfth:
       #ffffff on #BE185D  — 6.04:1  (button label, WCAG AA at any size)
       #A21550 on #ffffff  — 7.61:1  (--accent-text on a card, AAA)
       #F082B3 on #0f1115  — 7.69:1  (dark --accent-text, AAA)

     Rejected: #DB2777 (4.60:1 on white — clears AA by 0.1 and would be the
     ecosystem's most fragile accent), and #A21CAF fuchsia (better hue
     separation from campus, but sits 33° from member's violet and the two
     are harder to tell apart on a dashboard than rose and red are).       */
  --accent: #BE185D;
  --accent-2: #9D174D;
  --accent-bg: rgba(190,24,93,0.08);
  --accent-glow: rgba(190,24,93,0.20);
  --accent-light: #FBDDEA;
  --accent-text: #A21550;       /* 7.6:1 on white — WCAG AAA */
  --accent-contrast: #ffffff;   /* 6.0:1 on #BE185D — WCAG AA */
}

[data-platform="eko"] {
  /* Duke EkoCheras Marketplace — EkoVest Berhad. Ruled by Joel 2026-09-01.

     #F58220 is the EkoCheras brand orange, read from the mall's own logo
     (ekocherasmall.com/images/logo.png, which has exactly two opaque colours:
     #FFFFFF and #F58220). The site's CSS is a stock Bootstrap 4 theme and
     carries no brand value — the same trap the migs note records about the
     Astra globals. Read the logo, not the stylesheet, for a client brand.

     THIS BLOCK KNOWINGLY BREAKS THE 15 deg HUE RULE. #F58220 is hue 28 deg,
     5 deg from mall #D97706, 10 deg from tools, 12 deg from dgcs. Joel ruled
     brand fidelity over intra-ecosystem separation for this build: it is a
     client-named deployment for EkoVest, the audiences do not overlap, and no
     user sees this UI and M-EasyMall's side by side. DO NOT "FIX" THIS BY
     DRIFTING THE HUE. If a check or a reviewer flags the 5 deg gap, cite
     BUILD_PLAN §4.10 and leave it. The ecosystem already ships a 0 deg
     collision — migs 226 deg and school 226 deg, accepted deliberately as the
     two education platforms. If the rule is ever revisited, what gets
     revisited is its SCOPE (client-named builds vs the M-Easy* products), not
     this value.

     --accent is DARKENED from the brand orange at constant hue, and that is
     not cosmetic. Five components hardcode `color:#fff` DIRECTLY on
     `background: var(--accent)` and never read --accent-contrast:
     .topbar-brand-dot, .topbar-avatar, .sidebar-brand-dot, .page-btn.active,
     .step.active .step-dot. So the fill has to carry white whatever this block
     declares, and a dark --accent-contrast would be ignored by all five.
     #fff on #F58220 is 2.59:1 — below even AA-large, and worse than any accent
     the ecosystem ships (the floor is mall's 3.19). #B85908 is the same hue at
     4.71:1. The brand orange is not lost: it IS the dark-theme accent text.

     Margins are deliberate. Run 63 rejected #DB2777 for clearing AA by 0.1 as
     "the ecosystem's most fragile accent"; a bare >= 4.5 search returns
     #BC5B08, clearing by 0.01, which would be worse than the thing it refused.

     Reproduce every value here with:
         node design/measure-accent.js --block '#F58220' --name eko

     The dark --accent-text is the brand orange ITSELF, unmodified. It only
     had to clear --surface-2 (#263348), the LIGHTEST dark backdrop and so the
     tightest — `.panel-index on a quiet surface`. An earlier candidate
     (#ED730B) was derived against --bg and --surface only, cleared both at
     6.00 and 4.92, and failed §6 at 4.28:1 on --surface-2. measure-accent.js
     now reads all three; if you add a dark backdrop, add it there too.

     Contrast, measured not assumed:
       #ffffff on #B85908  — 4.71:1  (button label, WCAG AA)
       #A34F07 on #ffffff  — 5.71:1  (--accent-text on a card, WCAG AA)
       #F58220 on #0f172a  — 6.89:1  (dark --accent-text on --bg)
       #F58220 on #1e293b  — 5.65:1  (dark --accent-text on --surface)
       #F58220 on #263348  — 4.92:1  (dark --accent-text on --surface-2)    */
  --accent: #B85908;
  --accent-2: #9A4B07;
  --accent-bg: rgba(184,89,8,0.08);
  --accent-glow: rgba(184,89,8,0.20);
  --accent-light: #FBE4D0;
  --accent-text: #A34F07;       /* 5.7:1 on white — WCAG AA */
  --accent-contrast: #ffffff;   /* 4.7:1 on #B85908 — WCAG AA */
}

/* ── 4. DARK MODE ────────────────────────────────────────────────────── */
[data-theme="dark"] {
  --bg: #0f172a;
  --bg-soft: #1e293b;
  --surface: #1e293b;
  --surface-2: #263348;
  --border: #334155;
  --border-2: #475569;
  --text: #e2e8f0;
  --text-2: #94a3b8;
  /* 3.07:1 at #64748b. Same derivation as the light value, walked up. */
  --muted: #919eb0;
  /* THE STATUS COLOURS USED AS TEXT. Same split as --accent-text and for the
     same reason: --red is a fill that white sits on (.btn-danger) AND the
     colour of the word "error" on a tint, and one value cannot be both.
     Fills keep their shipped values; only text moves. */
  --red-text: #f26a6a;
  --green-text: #18b050;
  --amber-text: #e87f06;
  --blue-text: #5b9bef;
  --purple-text: #ab80f3;
  --brand: #020817;
  --brand-2: #0f172a;
  --brand-3: #1e293b;
  --shadow-sm: 0 1px 4px rgba(0,0,0,0.4);
  --shadow: 0 4px 20px rgba(0,0,0,0.5);
  --shadow-lg: 0 12px 40px rgba(0,0,0,0.6);
  /* §50 parity. The glow keeps its accent ring — that is the signal — but the
     ambient half goes darker, exactly as the other three shadows do here.
     The durations, easings and geometry tokens have no entry because they are
     theme-neutral: a 220ms transition and a 3px rail do not change in the
     dark, and a value repeated identically is a value that can drift.     */
  --shadow-glow: 0 0 0 4px var(--accent-glow), 0 10px 34px rgba(0,0,0,0.55);
  --surface-deep: #020817;
}
[data-theme="dark"][data-platform="commerce"] {
  --brown-dark: #e2d5cb;
  --brown-mid: #c4a98f;
  --cream: var(--bg);
  --gray-50: var(--bg-soft);
  --gray-100: var(--border);
  --gray-200: var(--border);
  --gray-400: var(--muted);
  --gray-600: var(--text-2);
  --gray-800: var(--text);
}

[data-theme="dark"][data-platform="school"] {
  --accent-light: rgba(30,64,175,0.25);
}

/* Dark-mode accent parity — --accent-light must not stay a light tint on a
   dark surface. One rule per platform, same shape for all 11. (This line
   said "all 9" while listing ten; corrected when migs made it eleven —
   count the rules, not the comment.)                                      */
[data-theme="dark"][data-platform="do"]       { --accent-light: rgba(26,115,232,0.22); --accent-text: #6da6f0; }
[data-theme="dark"][data-platform="tools"]    { --accent-light: rgba(232,98,42,0.22); --accent-text: #ee8e66; }
[data-theme="dark"][data-platform="commerce"] { --accent-light: rgba(22,163,74,0.22); --accent-text: #1bc75a; }
[data-theme="dark"][data-platform="halal"]    { --accent-light: rgba(13,148,136,0.22); --accent-text: #11beaf; }
[data-theme="dark"][data-platform="school"]   { --accent-light: rgba(30,64,175,0.28); --accent-text: #829aea; }
[data-theme="dark"][data-platform="member"]   { --accent-light: rgba(124,58,237,0.22); --accent-text: #b189f4; }
[data-theme="dark"][data-platform="mall"]     { --accent-light: rgba(217,119,6,0.22); --accent-text: #f98f14; }
[data-theme="dark"][data-platform="campus"]   { --accent-light: rgba(220,38,38,0.22); --accent-text: #ea7e7e; }
[data-theme="dark"][data-platform="esg"]      { --accent-light: rgba(77,124,15,0.22); --accent-text: #6fb316; }
[data-theme="dark"][data-platform="dgcs"]     { --accent-light: rgba(124,45,18,0.22); --accent-text: #e8815e; }
[data-theme="dark"][data-platform="migs"]     { --accent-light: rgba(52,72,137,0.22); --accent-text: #899ad2; }
[data-theme="dark"][data-platform="pos"]      { --accent-light: rgba(190,24,93,0.22); --accent-text: #f082b3; }
[data-theme="dark"][data-platform="eko"]      { --accent-light: rgba(184,89,8,0.22); --accent-text: #F58220; }

/* ── 5. RESET & BASE ─────────────────────────────────────────────────── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { font-family: var(--font); cursor: pointer; }
input, textarea, select { font-family: var(--font); }

/* ── 6. APP LAYOUT ───────────────────────────────────────────────────── */
.app-layout {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  grid-template-rows: var(--topbar-h) 1fr;
  height: 100vh;
  overflow: hidden;
}
.app-topbar {
  grid-column: 2;
  grid-row: 1;
  height: var(--topbar-h);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 12px;
  box-shadow: var(--shadow-sm);
  z-index: 50;
  flex-shrink: 0;
}
.app-sidebar {
  grid-column: 1;
  grid-row: 1 / 3;
  width: var(--sidebar-w);
  background: var(--brand);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  z-index: 40;
  position: relative;
}
.app-sidebar::after {
  content: '';
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 1px;
  background: linear-gradient(180deg, transparent, rgba(255,255,255,0.06) 30%, rgba(255,255,255,0.06) 70%, transparent);
  pointer-events: none;
}
.app-main {
  grid-column: 2;
  grid-row: 2;
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--bg);
  padding: 24px;
}

/* ── 7. TOPBAR COMPONENTS ────────────────────────────────────────────── */
.topbar-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.3px;
  flex-shrink: 0;
  text-decoration: none;
}
.topbar-brand-dot {
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--accent);
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 2px 8px var(--accent-glow);
}
.topbar-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
  flex: 1;
  letter-spacing: -0.2px;
}
.topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}
.topbar-avatar {
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  border: 2px solid var(--accent-glow);
  user-select: none;
}

/* ── 8. SIDEBAR COMPONENTS ───────────────────────────────────────────── */
.sidebar-logo {
  padding: 20px 16px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.07);
  flex-shrink: 0;
  text-decoration: none;
}
.sidebar-brand-dot {
  width: 34px; height: 34px;
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  font-size: 15px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 4px 12px var(--accent-glow);
}
.sidebar-brand {
  font-size: 16px;
  font-weight: 700;
  color: #fff;
  letter-spacing: -0.3px;
  line-height: 1.2;
}
.sidebar-brand em {
  font-style: normal;
  opacity: 0.6;
  font-size: 11px;
  display: block;
  font-weight: 500;
  letter-spacing: 0;
  margin-top: 1px;
}
.sidebar-section-label {
  padding: 16px 16px 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  /* 0.28 measured 2.50:1 light / 2.37:1 dark against --brand. 0.46 is the
     lowest alpha that clears 4.5 in both. */
  color: rgba(255,255,255,0.46);
}
.sidebar-nav {
  flex: 1;
  padding: 8px 0;
}
.sidebar-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  margin: 1px 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
  color: rgba(255,255,255,0.5);
  font-size: 13px;
  font-weight: 500;
  border: none;
  background: none;
  width: calc(100% - 16px);
  text-align: left;
  font-family: var(--font);
  transition: all 0.15s;
  position: relative;
  text-decoration: none;
}
.sidebar-item:hover {
  background: rgba(255,255,255,0.06);
  color: rgba(255,255,255,0.8);
}
.sidebar-item.active {
  background: var(--accent-bg);
  color: #fff;
}
.sidebar-item.active::before {
  content: '';
  position: absolute;
  left: -8px; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 18px;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}
.sidebar-item-icon {
  width: 18px;
  flex-shrink: 0;
  opacity: 0.7;
  font-size: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.sidebar-item.active .sidebar-item-icon { opacity: 1; }
.sidebar-item-badge {
  margin-left: auto;
  font-size: 10px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 20px;
  background: var(--accent-bg);
  color: var(--accent);
}
.sidebar-footer {
  margin-top: auto;
  padding: 12px 10px;
  border-top: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}
.sidebar-user {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background 0.15s;
  text-decoration: none;
}
.sidebar-user:hover { background: rgba(255,255,255,0.05); }
.sidebar-user-avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.sidebar-user-info { flex: 1; min-width: 0; }
.sidebar-user-name {
  font-size: 13px;
  font-weight: 600;
  color: rgba(255,255,255,0.85);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-user-plan {
  font-size: 11px;
  /* 0.35 measured 3.21:1 light / 3.10:1 dark against --brand. */
  color: rgba(255,255,255,0.46);
  margin-top: 1px;
}
.sidebar-logout {
  display: block;
  text-align: center;
  margin-top: 8px;
  padding: 7px;
  background: rgba(255,255,255,0.06);
  color: rgba(255,255,255,0.5);
  border-radius: var(--r-sm);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
  transition: all 0.15s;
}
.sidebar-logout:hover {
  background: rgba(255,255,255,0.12);
  color: #fff;
}

/* ── 9. BOTTOM NAV ───────────────────────────────────────────────────── */
.app-bottom-nav {
  display: none;
  position: fixed;
  bottom: 0; left: 0; right: 0;
  height: 60px;
  background: var(--surface);
  border-top: 1px solid var(--border);
  z-index: 100;
  box-shadow: 0 -4px 16px rgba(0,0,0,0.06);
}
.bottom-nav-inner {
  display: flex;
  height: 100%;
  align-items: stretch;
}
.bottom-nav-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  color: var(--muted);
  font-size: 10px;
  font-weight: 600;
  cursor: pointer;
  border: none;
  background: none;
  font-family: var(--font);
  text-decoration: none;
  transition: color 0.15s;
  letter-spacing: 0.2px;
}
.bottom-nav-item.active,
.bottom-nav-item:hover {
  color: var(--accent-text);
}
.bottom-nav-item-icon {
  width: 22px; height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ── 10. CARD ─────────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
}
.card-header {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.card-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  margin: 0;
}
.card-body { padding: 20px; }

/* ── 11. STAT CARDS ──────────────────────────────────────────────────── */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 14px;
  margin-bottom: 20px;
}
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 18px 20px;
  box-shadow: var(--shadow-sm);
  transition: all 0.2s;
  position: relative;
  overflow: hidden;
}
.stat-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--accent);
}
.stat-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.stat-icon {
  font-size: 22px;
  margin-bottom: 8px;
  display: block;
  position: absolute;
  top: 16px; right: 16px;
  opacity: 0.15;
  font-size: 28px;
}
.stat-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.stat-value {
  font-size: 26px;
  font-weight: 800;
  color: var(--text);
  line-height: 1;
  letter-spacing: -0.5px;
}
.stat-sub {
  font-size: 12px;
  color: var(--muted);
  margin-top: 4px;
}

/* ── 12. TABLE ────────────────────────────────────────────────────────── */
.table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: 20px;
}
.table-header {
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--surface-2);
}
.table-header h3 {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  margin: 0;
}
table { width: 100%; border-collapse: collapse; }
th {
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--muted);
  padding: 10px 16px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
td {
  padding: 12px 16px;
  font-size: 13px;
  color: var(--text-2);
  border-bottom: 1px solid var(--border);
}
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--bg-soft); }

/* ── 13. BUTTONS ─────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  border: none;
  font-family: var(--font);
  transition: all 0.18s;
  white-space: nowrap;
  text-decoration: none;
  line-height: 1;
}
.btn-primary {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 2px 8px var(--accent-bg);
}
.btn-primary:hover {
  background: var(--accent-2);
  box-shadow: 0 4px 14px var(--accent-glow);
  transform: translateY(-1px);
}
.btn-outline {
  background: var(--surface);
  color: var(--text-2);
  border: 1.5px solid var(--border);
}
.btn-outline:hover {
  background: var(--bg-soft);
  border-color: var(--border-2);
}
.btn-danger {
  background: var(--red);
  color: #fff;
}
.btn-danger:hover { opacity: 0.9; }
.btn-gold {
  background: var(--amber);
  color: #fff;
}
.btn-gold:hover { opacity: 0.9; }
.btn-sm { padding: 5px 12px; font-size: 12px; }
.btn-lg { padding: 11px 24px; font-size: 14px; }

/* ── 14. BADGES ──────────────────────────────────────────────────────── */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.badge-blue { background: var(--blue-bg); color: var(--blue-text); }
.badge-green { background: var(--green-bg); color: var(--green-text); }
.badge-red { background: var(--red-bg); color: var(--red-text); }
.badge-amber { background: var(--amber-bg); color: var(--amber-text); }
.badge-orange { background: var(--amber-bg); color: var(--amber-text); }
.badge-gold { background: var(--amber-bg); color: var(--amber-text); }
.badge-purple { background: var(--purple-bg); color: var(--purple-text); }
.badge-gray {
  background: var(--bg-soft);
  color: var(--text-2);
  border: 1px solid var(--border);
}
.badge-accent { background: var(--accent-bg); color: var(--accent-text); }

/* ── 15. FORMS ───────────────────────────────────────────────────────── */
.form-group {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 14px;
}
.form-group label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
}
.form-group input,
.form-group select,
.form-group textarea {
  padding: 9px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  font-size: 13px;
  transition: border-color 0.15s, box-shadow 0.15s;
  outline: none;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}

/* ── 16. MODAL ───────────────────────────────────────────────────────── */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 28px;
  width: 100%;
  max-width: 560px;
  position: relative;
  max-height: 90vh;
  overflow-y: auto;
}
.modal h2 {
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 20px;
}
.modal-title {
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 20px;
  display: block;
}
.modal-close {
  position: absolute;
  top: 16px; right: 16px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 50%;
  width: 30px; height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--muted);
  font-size: 16px;
  transition: all 0.15s;
  font-family: var(--font);
}
.modal-close:hover { background: var(--red-bg); color: var(--red-text); }

/* ── 17. SEARCH BAR ──────────────────────────────────────────────────── */
.search-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 7px 12px;
}
.search-bar input {
  border: none;
  background: transparent;
  outline: none;
  font-size: 13px;
  color: var(--text);
  width: 100%;
  font-family: var(--font);
}
.search-bar input::placeholder { color: var(--muted); }
.search-bar span { color: var(--muted); font-size: 14px; flex-shrink: 0; }

/* ── 18. AI INSIGHT BOX ──────────────────────────────────────────────── */
.ai-insight {
  background: linear-gradient(135deg, var(--accent-bg), var(--surface-2));
  border: 1.5px solid var(--accent-glow);
  border-radius: var(--r);
  padding: 16px 20px;
  margin-bottom: 20px;
}
.ai-insight-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--accent-text);
  margin-bottom: 8px;
}
.ai-insight p {
  font-size: 13px;
  color: var(--text-2);
  line-height: 1.6;
}

/* ── 19. EMPTY STATE ─────────────────────────────────────────────────── */
.empty-state {
  text-align: center;
  padding: 60px 24px;
}
.empty-state-icon {
  font-size: 48px;
  margin-bottom: 16px;
  line-height: 1;
  display: block;
}
.empty-state h3 {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 6px;
}
.empty-state p {
  font-size: 13px;
  color: var(--muted);
  max-width: 320px;
  margin: 0 auto;
  line-height: 1.6;
}

/* ── 20. COMING SOON ─────────────────────────────────────────────────── */
.coming-soon {
  text-align: center;
  padding: 80px 24px;
}
.coming-soon-icon {
  font-size: 56px;
  margin-bottom: 20px;
  line-height: 1;
  display: block;
}
.coming-soon h2 {
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 8px;
}
.coming-soon p {
  font-size: 14px;
  color: var(--muted);
  max-width: 400px;
  margin: 0 auto 20px;
  line-height: 1.6;
}
.coming-soon-phase {
  display: inline-flex;
  align-items: center;
  padding: 6px 16px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 700;
  background: var(--accent-bg);
  color: var(--accent-text);
  border: 1px solid var(--accent-glow);
}

/* ── 21. DARK MODE TOGGLE ────────────────────────────────────────────── */
.theme-toggle {
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-soft);
  color: var(--muted);
  cursor: pointer;
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
  flex-shrink: 0;
  font-family: var(--font);
}
.theme-toggle:hover {
  background: var(--accent-bg);
  color: var(--accent-text);
  border-color: var(--accent-glow);
}

/* Inline theme toggle script — include on any page to wire up toggleTheme() */

/* ── 22. RESPONSIVE ──────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .app-layout {
    grid-template-columns: 1fr;
    grid-template-rows: var(--topbar-h) 1fr;
  }
  .app-sidebar { display: none; }
  .app-topbar { grid-column: 1; }
  .app-main {
    grid-column: 1;
    padding: 16px;
    padding-bottom: 72px;
  }
  .app-bottom-nav { display: block; }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .form-grid { grid-template-columns: 1fr; }

  /* §6: "Touch targets ≥ 44×44 px on mobile". Nothing in this file cleared it
     until Run 54 — .btn measured ~29px and .btn-sm ~22px, and a 22px target on
     a phone is a miss, not a tap. Applied HERE and not to the base rule on
     purpose: 44px is a finger, not a design, and a desktop toolbar of 44px
     buttons is a different and worse product. min-height rather than height so
     a button that wraps still grows. */
  .btn,
  .btn-sm,
  .btn-lg,
  .btn-oauth,
  .page-btn,
  .theme-toggle,
  .modal-close,
  .pw-toggle {
    min-height: 44px;
    min-width: 44px;
  }
}
@media (max-width: 480px) {
  .stat-grid { grid-template-columns: 1fr 1fr; }
  .app-main { padding: 12px; padding-bottom: 72px; }
  .modal { padding: 20px; }
}

/* ── 23. BACKWARD-COMPAT ALIASES ────────────────────────────────────── */
/* These preserve existing inline class names while new code uses new names */
.es-icon { font-size: 48px; margin-bottom: 16px; display: block; }
.cs-icon { font-size: 56px; margin-bottom: 20px; display: block; }
.phase-tag {
  display: inline-flex;
  align-items: center;
  padding: 6px 16px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 700;
  background: var(--accent-bg);
  color: var(--accent-text);
  border: 1px solid var(--accent-glow);
}
.table-card { /* old name → use .table-wrap */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: 20px;
}
.ai-box { /* old name → use .ai-insight */
  background: linear-gradient(135deg, var(--accent-bg), var(--surface-2));
  border: 1.5px solid var(--accent-glow);
  border-radius: var(--r);
  padding: 16px 20px;
  margin-bottom: 20px;
}
.ai-box-header { /* old name → use .ai-insight-header */
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--accent-text);
  margin-bottom: 8px;
}
.ai-box p {
  font-size: 13px;
  color: var(--text-2);
  line-height: 1.6;
}

/* ── 24. SCROLLBAR ───────────────────────────────────────────────────── */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }
* { scrollbar-width: thin; scrollbar-color: var(--border-2) transparent; }

/* ═══════════════════════════════════════════════════════════════════════════
   MODUS DESIGN SYSTEM v2 — Production Component Extensions
   ─────────────────────────────────────────────────────────────────────────
   New in v2:
   25  Typography scale       30  Progress bar         35  Dropdown menu
   26  Skeleton loading        31  Tabs                 36  Pagination
   27  Spinner                 32  Breadcrumbs          37  Avatar group
   28  Toast / Notification    33  Toggle switch        38  Stepper
   29  Alert / Banner          34  Tooltip              39  Tag / Chip
                                                        40  Divider
                                                        41  Focus / A11y
                                                        42  Animations
                                                        43  Utilities
═══════════════════════════════════════════════════════════════════════════ */

/* ── 25. TYPOGRAPHY SCALE ────────────────────────────────────────────── */
.text-xs    { font-size: 11px; line-height: 1.5; }
.text-sm    { font-size: 12px; line-height: 1.5; }
.text-base  { font-size: 14px; line-height: 1.6; }
.text-md    { font-size: 15px; line-height: 1.6; }
.text-lg    { font-size: 17px; line-height: 1.5; }
.text-xl    { font-size: 20px; line-height: 1.4; }
.text-2xl   { font-size: 24px; line-height: 1.3; }
.text-3xl   { font-size: 30px; line-height: 1.2; }

.font-normal  { font-weight: 400; }
.font-medium  { font-weight: 500; }
.font-semibold{ font-weight: 600; }
.font-bold    { font-weight: 700; }
.font-extrabold{ font-weight: 800; }

.text-primary { color: var(--text); }
.text-secondary{ color: var(--text-2); }
.text-muted   { color: var(--muted); }
.text-accent  { color: var(--accent-text); }
.text-success { color: var(--green-text); }
.text-warning { color: var(--amber-text); }
.text-danger  { color: var(--red-text); }

.truncate { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.leading-tight { line-height: 1.2; }
.leading-normal{ line-height: 1.5; }
.leading-relaxed{ line-height: 1.7; }
.tracking-tight { letter-spacing: -0.4px; }
.tracking-wide  { letter-spacing: 0.6px; }
.uppercase { text-transform: uppercase; }

/* Page-level headings */
.page-title {
  font-size: 20px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: -0.4px;
  line-height: 1.2;
}
.page-subtitle {
  font-size: 13px;
  color: var(--muted);
  margin-top: 3px;
}
.section-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.2px;
}

/* ── 26. SKELETON LOADING ────────────────────────────────────────────── */
@keyframes mds-shimmer {
  0%   { background-position: -400px 0; }
  100% { background-position: 400px 0; }
}

.skeleton {
  background: linear-gradient(
    90deg,
    var(--border) 25%,
    var(--bg-soft) 50%,
    var(--border) 75%
  );
  background-size: 800px 100%;
  animation: mds-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-sm);
  display: block;
}
.skeleton-text   { height: 14px; width: 100%; margin-bottom: 8px; }
.skeleton-text-sm{ height: 12px; width: 70%; margin-bottom: 6px; }
.skeleton-title  { height: 20px; width: 45%; margin-bottom: 12px; }
.skeleton-avatar { width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0; }
.skeleton-btn    { height: 34px; width: 90px; }
.skeleton-badge  { height: 22px; width: 60px; border-radius: 20px; }
.skeleton-card   { height: 100px; width: 100%; }
.skeleton-table-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 0.5fr;
  gap: 16px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  align-items: center;
}
.skeleton-table-row .skeleton { margin-bottom: 0; }

/* Skeleton container — wraps the whole loading state */
.skeleton-page {
  animation: mds-fade-in 0.2s ease;
}
.skeleton-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 14px;
  margin-bottom: 20px;
}
.skeleton-stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* ── 27. SPINNER ─────────────────────────────────────────────────────── */
@keyframes mds-spin {
  to { transform: rotate(360deg); }
}

.spinner {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2.5px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: mds-spin 0.65s linear infinite;
  flex-shrink: 0;
}
.spinner-sm { width: 14px; height: 14px; border-width: 2px; }
.spinner-lg { width: 32px; height: 32px; border-width: 3px; }
.spinner-xl { width: 48px; height: 48px; border-width: 4px; }
.spinner-white { border-color: rgba(255,255,255,0.3); border-top-color: #fff; }

/* Button loading state */
.btn[data-loading="true"] {
  opacity: 0.75;
  pointer-events: none;
  position: relative;
}
.btn[data-loading="true"]::before {
  content: '';
  display: inline-block;
  width: 13px; height: 13px;
  border: 2px solid rgba(255,255,255,0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: mds-spin 0.65s linear infinite;
  margin-right: 6px;
  flex-shrink: 0;
}
.btn-outline[data-loading="true"]::before {
  border-color: var(--border-2);
  border-top-color: var(--accent);
}

/* Full-page loader overlay */
.page-loader {
  position: fixed;
  inset: 0;
  background: rgba(var(--bg), 0.85);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  flex-direction: column;
  gap: 16px;
}
.page-loader p {
  font-size: 13px;
  color: var(--muted);
  font-weight: 500;
}

/* ── 28. TOAST / NOTIFICATION ────────────────────────────────────────── */
#mds-toast-region {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 9998;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 380px;
  width: calc(100vw - 48px);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 14px 16px;
  box-shadow: var(--shadow-lg);
  pointer-events: all;
  position: relative;
  overflow: hidden;
  animation: mds-toast-in 0.28s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.toast.toast-leaving {
  animation: mds-toast-out 0.22s ease-in both;
}

@keyframes mds-toast-in {
  from { opacity: 0; transform: translateX(100%) scale(0.95); }
  to   { opacity: 1; transform: translateX(0)   scale(1);    }
}
@keyframes mds-toast-out {
  from { opacity: 1; transform: translateX(0); max-height: 120px; margin-bottom: 0; }
  to   { opacity: 0; transform: translateX(100%); max-height: 0; margin-bottom: -10px; padding: 0; }
}

.toast::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  border-radius: 3px 0 0 3px;
}
.toast-success::before { background: var(--green); }
.toast-error::before   { background: var(--red); }
.toast-warning::before { background: var(--amber); }
.toast-info::before    { background: var(--accent); }

.toast-icon {
  font-size: 18px;
  flex-shrink: 0;
  width: 22px;
  text-align: center;
  line-height: 1;
  margin-top: 1px;
}
.toast-body { flex: 1; min-width: 0; }
.toast-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 2px;
  line-height: 1.3;
}
.toast-msg {
  font-size: 12px;
  color: var(--text-2);
  line-height: 1.5;
}
.toast-action {
  display: inline-flex;
  margin-top: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-text);
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.toast-close {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 0;
  flex-shrink: 0;
  transition: color 0.15s;
  font-family: var(--font);
  margin-top: -2px;
}
.toast-close:hover { color: var(--text); }

.toast-progress {
  position: absolute;
  bottom: 0; left: 0;
  height: 2px;
  background: var(--accent);
  opacity: 0.35;
  animation: mds-toast-progress linear both;
  transform-origin: left;
}
.toast-success .toast-progress { background: var(--green); }
.toast-error .toast-progress   { background: var(--red); }
.toast-warning .toast-progress { background: var(--amber); }

@keyframes mds-toast-progress {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}

/* ── 29. ALERT / BANNER ──────────────────────────────────────────────── */
.alert {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--r);
  border: 1px solid;
  margin-bottom: 16px;
  font-size: 13px;
  line-height: 1.5;
}
.alert-icon {
  font-size: 16px;
  flex-shrink: 0;
  margin-top: 1px;
}
.alert-body { flex: 1; }
.alert-title {
  font-weight: 700;
  margin-bottom: 3px;
  font-size: 13px;
}
.alert-close {
  background: none; border: none;
  cursor: pointer; color: inherit;
  opacity: 0.6; font-size: 16px;
  padding: 0; line-height: 1;
  flex-shrink: 0;
  font-family: var(--font);
  transition: opacity 0.15s;
}
.alert-close:hover { opacity: 1; }

.alert-info    { background: var(--blue-bg);   border-color: rgba(26,115,232,0.2);   color: var(--blue-text);   }
.alert-success { background: var(--green-bg);  border-color: rgba(22,163,74,0.2);   color: var(--green-text);  }
.alert-warning { background: var(--amber-bg);  border-color: rgba(217,119,6,0.2);    color: var(--amber-text);  }
.alert-error   { background: var(--red-bg);    border-color: rgba(239,68,68,0.2);    color: var(--red-text);    }

.alert-info .alert-body,
.alert-success .alert-body,
.alert-warning .alert-body,
.alert-error .alert-body { color: var(--text); }

/* Inline alert — tighter */
.alert-inline {
  padding: 8px 12px;
  border-radius: var(--r-sm);
  gap: 8px;
  margin-bottom: 10px;
}

/* ── 30. PROGRESS BAR ────────────────────────────────────────────────── */
.progress {
  width: 100%;
  height: 6px;
  background: var(--border);
  border-radius: 99px;
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 99px;
  transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.progress-sm { height: 4px; }
.progress-lg { height: 10px; }

.progress-success .progress-bar { background: var(--green); }
.progress-warning .progress-bar { background: var(--amber); }
.progress-danger  .progress-bar { background: var(--red); }

/* Labeled progress */
.progress-labeled {
  display: flex;
  align-items: center;
  gap: 10px;
}
.progress-labeled .progress { flex: 1; }
.progress-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
  min-width: 36px;
  text-align: right;
}

/* Indeterminate / animate progress */
.progress-indeterminate .progress-bar {
  width: 40% !important;
  animation: mds-progress-slide 1.4s ease-in-out infinite;
}
@keyframes mds-progress-slide {
  0%   { transform: translateX(-150%); }
  100% { transform: translateX(350%); }
}

/* ── 31. TABS ────────────────────────────────────────────────────────── */
.tabs {
  display: flex;
  border-bottom: 2px solid var(--border);
  gap: 0;
  margin-bottom: 20px;
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 18px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  cursor: pointer;
  white-space: nowrap;
  background: none;
  border-top: none;
  border-left: none;
  border-right: none;
  font-family: var(--font);
  transition: all 0.15s;
  text-decoration: none;
}
.tab:hover { color: var(--text-2); }
.tab.active {
  color: var(--accent-text);
  border-bottom-color: var(--accent);
}
.tab-badge {
  font-size: 10px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 99px;
  background: var(--bg-soft);
  color: var(--text-2);
  border: 1px solid var(--border);
}
.tab.active .tab-badge {
  background: var(--accent-bg);
  color: var(--accent-text);
  border-color: var(--accent-glow);
}

/* Pill tabs variant */
.tabs-pill {
  border-bottom: none;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 4px;
  display: inline-flex;
  gap: 2px;
  margin-bottom: 20px;
}
.tabs-pill .tab {
  border-bottom: none;
  border-radius: var(--r-sm);
  padding: 7px 16px;
  margin-bottom: 0;
  font-size: 12px;
}
.tabs-pill .tab.active {
  background: var(--surface);
  color: var(--text);
  border-bottom-color: transparent;
  box-shadow: var(--shadow-sm);
}
.tabs-pill .tab:not(.active):hover {
  background: rgba(0,0,0,0.04);
  color: var(--text-2);
}

/* Tab panel */
.tab-panel { display: none; animation: mds-fade-in 0.2s ease; }
.tab-panel.active { display: block; }

/* ── 32. BREADCRUMBS ─────────────────────────────────────────────────── */
.breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 16px;
  font-size: 12px;
}
.breadcrumb-item {
  color: var(--muted);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.15s;
}
.breadcrumb-item:hover { color: var(--accent-text); }
.breadcrumb-item:last-child {
  color: var(--text);
  font-weight: 600;
  pointer-events: none;
}
.breadcrumb-sep {
  color: var(--border-2);
  font-size: 11px;
  flex-shrink: 0;
}

/* ── 33. TOGGLE / SWITCH ─────────────────────────────────────────────── */
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  user-select: none;
}
.toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0; height: 0;
}
.toggle-track {
  position: relative;
  width: 40px; height: 22px;
  background: var(--border-2);
  border-radius: 99px;
  transition: background 0.2s;
  flex-shrink: 0;
}
.toggle-track::after {
  content: '';
  position: absolute;
  left: 3px; top: 3px;
  width: 16px; height: 16px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}
.toggle input:checked + .toggle-track {
  background: var(--accent);
}
.toggle input:checked + .toggle-track::after {
  transform: translateX(18px);
}
.toggle input:focus-visible + .toggle-track {
  box-shadow: 0 0 0 3px var(--accent-bg);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.toggle input:disabled + .toggle-track {
  opacity: 0.45;
  cursor: not-allowed;
}
.toggle-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}
.toggle-sm .toggle-track { width: 32px; height: 18px; }
.toggle-sm .toggle-track::after { width: 12px; height: 12px; }
.toggle-sm input:checked + .toggle-track::after { transform: translateX(14px); }

/* Checkbox */
.checkbox {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 13px;
  color: var(--text);
  font-weight: 500;
}
.checkbox input[type="checkbox"] {
  width: 16px; height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
  border-radius: 4px;
  flex-shrink: 0;
}

/* Radio */
.radio {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 13px;
  color: var(--text);
  font-weight: 500;
}
.radio input[type="radio"] {
  width: 16px; height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

/* ── 34. TOOLTIP ─────────────────────────────────────────────────────── */
[data-tooltip] { position: relative; }

[data-tooltip]::before,
[data-tooltip]::after {
  position: absolute;
  z-index: 9000;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s, transform 0.15s;
}
[data-tooltip]::before {
  content: attr(data-tooltip);
  background: var(--brand);
  color: rgba(255,255,255,0.9);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.4;
  padding: 6px 10px;
  border-radius: var(--r-sm);
  white-space: nowrap;
  max-width: 240px;
  white-space: normal;
  text-align: center;
  box-shadow: var(--shadow);
  font-family: var(--font);
  letter-spacing: 0;
}
[data-tooltip]::after {
  content: '';
  border: 5px solid transparent;
}

/* Tooltip — top (default) */
[data-tooltip]:not([data-tip-pos])::before,
[data-tooltip][data-tip-pos="top"]::before {
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
}
[data-tooltip]:not([data-tip-pos])::after,
[data-tooltip][data-tip-pos="top"]::after {
  bottom: calc(100% + 0px);
  left: 50%;
  transform: translateX(-50%);
  border-top-color: var(--brand);
}

/* Tooltip — bottom */
[data-tooltip][data-tip-pos="bottom"]::before {
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
}
[data-tooltip][data-tip-pos="bottom"]::after {
  top: calc(100%);
  left: 50%;
  transform: translateX(-50%);
  border-bottom-color: var(--brand);
}

/* Tooltip — right */
[data-tooltip][data-tip-pos="right"]::before {
  left: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%) translateX(-4px);
}
[data-tooltip][data-tip-pos="right"]::after {
  left: calc(100%);
  top: 50%;
  transform: translateY(-50%);
  border-right-color: var(--brand);
}

/* Tooltip — left */
[data-tooltip][data-tip-pos="left"]::before {
  right: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%) translateX(4px);
}
[data-tooltip][data-tip-pos="left"]::after {
  right: calc(100%);
  top: 50%;
  transform: translateY(-50%);
  border-left-color: var(--brand);
}

/* Show on hover */
[data-tooltip]:hover::before,
[data-tooltip]:hover::after {
  opacity: 1;
}
[data-tooltip]:not([data-tip-pos]):hover::before,
[data-tooltip][data-tip-pos="top"]:hover::before {
  transform: translateX(-50%) translateY(0);
}
[data-tooltip][data-tip-pos="bottom"]:hover::before {
  transform: translateX(-50%) translateY(0);
}
[data-tooltip][data-tip-pos="right"]:hover::before {
  transform: translateY(-50%) translateX(0);
}
[data-tooltip][data-tip-pos="left"]:hover::before {
  transform: translateY(-50%) translateX(0);
}

/* ── 35. DROPDOWN MENU ───────────────────────────────────────────────── */
.dropdown { position: relative; display: inline-block; }

.dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 180px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
  z-index: 500;
  padding: 5px;
  opacity: 0;
  transform: translateY(-6px) scale(0.97);
  pointer-events: none;
  transition: opacity 0.15s, transform 0.15s;
  transform-origin: top left;
}
.dropdown-menu.open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: all;
}
.dropdown-menu-right { left: auto; right: 0; transform-origin: top right; }

.dropdown-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
  font-family: var(--font);
  text-decoration: none;
  transition: all 0.12s;
}
.dropdown-item:hover {
  background: var(--bg-soft);
  color: var(--text);
}
.dropdown-item.active {
  background: var(--accent-bg);
  color: var(--accent-text);
}
.dropdown-item-icon {
  width: 16px;
  text-align: center;
  font-size: 14px;
  flex-shrink: 0;
}
.dropdown-item-danger { color: var(--red-text); }
.dropdown-item-danger:hover { background: var(--red-bg); color: var(--red-text); }

.dropdown-sep {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}
.dropdown-label {
  padding: 6px 12px 3px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
}

/* ── 36. PAGINATION ──────────────────────────────────────────────────── */
.pagination {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}
.page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface);
  border: 1.5px solid var(--border);
  cursor: pointer;
  font-family: var(--font);
  transition: all 0.15s;
  text-decoration: none;
  user-select: none;
}
.page-btn:hover { background: var(--bg-soft); border-color: var(--border-2); color: var(--text); }
.page-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 2px 8px var(--accent-glow);
}
.page-btn:disabled,
.page-btn[aria-disabled="true"] {
  opacity: 0.35;
  pointer-events: none;
}
.page-btn-dots {
  background: none;
  border: none;
  color: var(--muted);
  pointer-events: none;
}

.pagination-info {
  font-size: 12px;
  color: var(--muted);
  margin-left: 8px;
  white-space: nowrap;
}

/* ── 37. AVATAR GROUP ────────────────────────────────────────────────── */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 700;
  font-size: 12px;
  color: #fff;
  background: var(--accent);
  flex-shrink: 0;
  user-select: none;
  position: relative;
  overflow: hidden;
}
.avatar img {
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
.avatar-xs  { width: 24px; height: 24px; font-size: 10px; }
.avatar-sm  { width: 32px; height: 32px; font-size: 11px; }
.avatar-md  { width: 40px; height: 40px; font-size: 14px; }
.avatar-lg  { width: 48px; height: 48px; font-size: 16px; }
.avatar-xl  { width: 64px; height: 64px; font-size: 22px; }
.avatar-2xl { width: 80px; height: 80px; font-size: 28px; }

.avatar-square { border-radius: var(--r-sm); }

/* Status dot */
.avatar-status {
  position: absolute;
  bottom: 1px; right: 1px;
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 2px solid var(--surface);
}
.status-online  { background: var(--green); }
.status-offline { background: var(--muted); }
.status-busy    { background: var(--red); }
.status-away    { background: var(--amber); }

/* Avatar group */
.avatar-group {
  display: flex;
  align-items: center;
}
.avatar-group .avatar {
  border: 2px solid var(--surface);
  margin-left: -8px;
  transition: transform 0.15s;
}
.avatar-group .avatar:first-child { margin-left: 0; }
.avatar-group .avatar:hover { transform: translateY(-2px); z-index: 1; }
.avatar-group-count {
  background: var(--bg-soft);
  border: 2px solid var(--border);
  color: var(--text-2);
  font-size: 11px;
  font-weight: 700;
  width: 32px; height: 32px;
  margin-left: -8px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ── 38. STEPPER ─────────────────────────────────────────────────────── */
.stepper {
  display: flex;
  align-items: flex-start;
  gap: 0;
  margin-bottom: 24px;
  overflow-x: auto;
  padding-bottom: 4px;
}
.step {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  position: relative;
  min-width: 80px;
}
.step:not(:last-child)::after {
  content: '';
  position: absolute;
  top: 16px;
  left: calc(50% + 16px);
  right: calc(-50% + 16px);
  height: 2px;
  background: var(--border);
  z-index: 0;
}
.step.done:not(:last-child)::after  { background: var(--accent); }

.step-dot {
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: var(--surface);
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  position: relative;
  transition: all 0.2s;
  flex-shrink: 0;
}
.step.active .step-dot {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  box-shadow: 0 0 0 4px var(--accent-bg);
}
.step.done .step-dot {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}
.step-label {
  margin-top: 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-align: center;
}
.step.active .step-label { color: var(--accent-text); }
.step.done .step-label   { color: var(--text-2); }

/* ── 39. TAG / CHIP ──────────────────────────────────────────────────── */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 99px;
  font-size: 12px;
  font-weight: 600;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  color: var(--text-2);
  white-space: nowrap;
}
.tag-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
  line-height: 1;
  font-family: var(--font);
  transition: all 0.12s;
  margin-right: -3px;
}
.tag-close:hover {
  background: var(--border-2);
  color: var(--text);
}
.tag-blue   { background: var(--blue-bg);   color: var(--blue-text);   border-color: rgba(26,115,232,0.2); }
.tag-green  { background: var(--green-bg);  color: var(--green-text);  border-color: rgba(22,163,74,0.2); }
.tag-red    { background: var(--red-bg);    color: var(--red-text);    border-color: rgba(239,68,68,0.2); }
.tag-amber  { background: var(--amber-bg);  color: var(--amber-text);  border-color: rgba(217,119,6,0.2); }
.tag-purple { background: var(--purple-bg); color: var(--purple-text); border-color: rgba(124,58,237,0.2); }
.tag-accent { background: var(--accent-bg); color: var(--accent-text); border-color: var(--accent-glow); }

/* Tag input area */
.tag-input-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  padding: 7px 10px;
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  transition: border-color 0.15s, box-shadow 0.15s;
  cursor: text;
  min-height: 40px;
}
.tag-input-wrap:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
.tag-input-wrap input {
  border: none; outline: none;
  background: transparent;
  font-size: 13px;
  color: var(--text);
  min-width: 80px;
  font-family: var(--font);
  flex: 1;
}

/* ── 40. DIVIDER ─────────────────────────────────────────────────────── */
.divider {
  height: 1px;
  background: var(--border);
  margin: 16px 0;
}
.divider-label {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0;
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.8px;
}
.divider-label::before,
.divider-label::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}
.divider-vertical {
  display: inline-block;
  width: 1px;
  height: 1em;
  background: var(--border);
  margin: 0 8px;
  vertical-align: middle;
}

/* ── 41. FOCUS & ACCESSIBILITY ───────────────────────────────────────── */
/* Visible focus ring — only when navigating by keyboard */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
:focus:not(:focus-visible) { outline: none; }

/* Skip-to-content link */
.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  background: var(--accent);
  color: #fff;
  padding: 8px 16px;
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  z-index: 10000;
  transition: top 0.1s;
  text-decoration: none;
}
.skip-link:focus { top: 0; }

/* Screen reader only */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border-width: 0;
}

/* High-contrast mode support */
@media (forced-colors: active) {
  .btn-primary  { forced-color-adjust: none; }
  .badge        { forced-color-adjust: none; }
}

/* Reduce motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .skeleton { animation: none; background: var(--border); }

  /* §50. THE RULE ABOVE IS EXACTLY WHAT MAKES AN ENTRANCE STATE DANGEROUS,
     so the fix belongs here, in the one block, and not in a second one whose
     effect would depend on source order.

     An entrance class whose resting state is opacity:0 and which is revealed
     by motion does not become "fast" under this media query — it becomes
     PERMANENTLY INVISIBLE, and the content is never reachable. That is the
     accessibility failure, not a cosmetic one.

     So .reveal collapses to its FINAL state — visible, untransformed — rather
     than to a fast animation. Nothing is removed and nothing is hidden.     */
  [data-reveal="on"] .reveal,
  [data-reveal="on"] .reveal.is-visible {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ── 42. ANIMATIONS & TRANSITIONS ───────────────────────────────────── */
@keyframes mds-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes mds-fade-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes mds-fade-down {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes mds-scale-in {
  from { opacity: 0; transform: scale(0.94); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes mds-slide-in-right {
  from { opacity: 0; transform: translateX(20px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes mds-pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.5; }
}

.animate-fade-in        { animation: mds-fade-in   0.2s ease both; }
.animate-fade-up        { animation: mds-fade-up   0.25s ease both; }
.animate-fade-down      { animation: mds-fade-down 0.2s ease both; }
.animate-scale-in       { animation: mds-scale-in  0.2s ease both; }
.animate-slide-right    { animation: mds-slide-in-right 0.25s ease both; }
.animate-pulse          { animation: mds-pulse 2s ease infinite; }
.animate-spin           { animation: mds-spin  1s linear infinite; }

/* Staggered children */
.stagger > * { animation: mds-fade-up 0.25s ease both; }
.stagger > *:nth-child(1) { animation-delay: 0.05s; }
.stagger > *:nth-child(2) { animation-delay: 0.10s; }
.stagger > *:nth-child(3) { animation-delay: 0.15s; }
.stagger > *:nth-child(4) { animation-delay: 0.20s; }
.stagger > *:nth-child(5) { animation-delay: 0.25s; }
.stagger > *:nth-child(6) { animation-delay: 0.30s; }

/* ── 43. UTILITY CLASSES ─────────────────────────────────────────────── */
/* Display */
.flex       { display: flex; }
.inline-flex{ display: inline-flex; }
.grid       { display: grid; }
.block      { display: block; }
.hidden     { display: none !important; }

/* The hidden ATTRIBUTE, not the class above.

   The browser's own `[hidden] { display: none }` lives in the UA origin, and
   EVERY author declaration outranks that origin however weak its selector. So
   any one of the ~150 rules in this sheet that declares a display — .form-group,
   .field-error, .auth-tabs, .alert, .modal-footer, .settings-row, .chip-row … —
   silently defeats it. The element keeps hidden="", `el.hidden` still reads
   true, and it renders anyway.

   Two measured instances, both shipped:

     #regRoleGroup — the register portal's "I am signing up as" selector, a
     .form-group{display:flex} that §48's script unhides only when the platform
     declares more than one self-service role. M-EasyPOS declares none, so the
     branch never ran and the row rendered 85px tall and required-looking on
     every POS signup, above the invite-code field people are meant to use.

     .auth-tabs — {display:grid}. showView() sets .auth-tabs.hidden = true on
     the forgot-password and OTP views; the tabs stayed 44px tall on all
     thirteen platforms.

   §49 already patched ONE of these per-element (.settings-savebar[hidden],
   left below and now redundant). A per-element exemption fixes the instance and
   leaves the bug waiting behind the next hidden component, which is how the two
   above shipped. So: the rule, not the exemption.

   `!important` because the whole point is to outrank an author `display`; the
   .hidden class above already uses it for the same reason. Nothing in the
   ecosystem shows a [hidden] element via inline style.display — verified across
   all thirteen repos before this landed — so nothing legitimate loses. */
[hidden]    { display: none !important; }

/* Flex */
.flex-col       { flex-direction: column; }
.flex-wrap      { flex-wrap: wrap; }
.items-center   { align-items: center; }
.items-start    { align-items: flex-start; }
.items-end      { align-items: flex-end; }
.justify-center { justify-content: center; }
.justify-between{ justify-content: space-between; }
.justify-end    { justify-content: flex-end; }
.flex-1         { flex: 1; }
.flex-shrink-0  { flex-shrink: 0; }

/* Gap */
.gap-1  { gap: 4px; }
.gap-2  { gap: 8px; }
.gap-3  { gap: 12px; }
.gap-4  { gap: 16px; }
.gap-5  { gap: 20px; }
.gap-6  { gap: 24px; }

/* Spacing */
.m-0  { margin: 0; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 4px; }
.mb-2 { margin-bottom: 8px; }
.mb-3 { margin-bottom: 12px; }
.mb-4 { margin-bottom: 16px; }
.mb-5 { margin-bottom: 20px; }
.mb-6 { margin-bottom: 24px; }
.mt-2 { margin-top: 8px; }
.mt-4 { margin-top: 16px; }
.mt-auto { margin-top: auto; }
.ml-auto { margin-left: auto; }

.p-0  { padding: 0; }
.p-3  { padding: 12px; }
.p-4  { padding: 16px; }
.p-5  { padding: 20px; }
.px-3 { padding-left: 12px; padding-right: 12px; }
.px-4 { padding-left: 16px; padding-right: 16px; }
.py-2 { padding-top: 8px; padding-bottom: 8px; }
.py-3 { padding-top: 12px; padding-bottom: 12px; }

/* Width / Height */
.w-full  { width: 100%; }
.h-full  { height: 100%; }
.min-w-0 { min-width: 0; }

/* Border radius */
.rounded-sm { border-radius: var(--r-sm); }
.rounded    { border-radius: var(--r); }
.rounded-lg { border-radius: var(--r-lg); }
.rounded-full { border-radius: 99px; }

/* Background / Surface */
.bg-surface  { background: var(--surface); }
.bg-soft     { background: var(--bg-soft); }
.bg-accent   { background: var(--accent); }
.border      { border: 1px solid var(--border); }
.border-none { border: none; }

/* Overflow */
.overflow-hidden { overflow: hidden; }
.overflow-auto   { overflow: auto; }

/* Cursor */
.cursor-pointer { cursor: pointer; }
.cursor-default { cursor: default; }
.pointer-events-none { pointer-events: none; }
.select-none { user-select: none; }

/* Position */
.relative { position: relative; }
.absolute { position: absolute; }
.fixed    { position: fixed; }
.inset-0  { inset: 0; }
.z-10     { z-index: 10; }
.z-50     { z-index: 50; }

/* Opacity */
.opacity-0   { opacity: 0; }
.opacity-50  { opacity: 0.5; }
.opacity-75  { opacity: 0.75; }
.opacity-100 { opacity: 1; }

/* Text align */
.text-center { text-align: center; }
.text-right  { text-align: right; }
.text-left   { text-align: left; }

/* Transition */
.transition     { transition: all 0.15s ease; }
.transition-200 { transition: all 0.2s ease; }

/* Input variants */
.input-error { border-color: var(--red) !important; }
.input-error:focus { box-shadow: 0 0 0 3px var(--red-bg) !important; }
.input-success { border-color: var(--green) !important; }
.field-error {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--red-text);
  margin-top: 4px;
  font-weight: 500;
}
.field-hint {
  font-size: 11px;
  color: var(--muted);
  margin-top: 4px;
}

/* ── 44. ENHANCED MODAL ──────────────────────────────────────────────── */
/* Dialog-level focus trap — modal gets aria-modal + role=dialog from JS */
.modal-overlay {
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.modal-overlay.open {
  animation: mds-fade-in 0.15s ease both;
}
.modal-overlay.open .modal {
  animation: mds-scale-in 0.2s cubic-bezier(0.34, 1.3, 0.64, 1) both;
}
.modal-footer {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.modal-sm { max-width: 380px; }
.modal-lg { max-width: 720px; }
.modal-xl { max-width: 960px; }
.modal-full { max-width: calc(100vw - 40px); max-height: calc(100vh - 40px); }

/* Drawer / side sheet */
.drawer-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  backdrop-filter: blur(2px);
  z-index: 900;
}
.drawer-overlay.open { display: flex; justify-content: flex-end; }
.drawer {
  background: var(--surface);
  width: 420px;
  max-width: 95vw;
  height: 100%;
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: mds-slide-in-right 0.25s ease both;
}
.drawer-header {
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}
.drawer-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
}
.drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: 20px 24px;
}
.drawer-footer {
  padding: 16px 24px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  flex-shrink: 0;
}

/* ── 45. TABLE ENHANCEMENTS ──────────────────────────────────────────── */
/* Sortable column headers */
th[data-sort] {
  cursor: pointer;
  user-select: none;
  position: relative;
  padding-right: 24px;
}
th[data-sort]::after {
  content: '↕';
  position: absolute;
  right: 8px;
  font-size: 10px;
  color: var(--muted);
  opacity: 0.5;
}
th[data-sort="asc"]::after  { content: '↑'; opacity: 1; color: var(--accent-text); }
th[data-sort="desc"]::after { content: '↓'; opacity: 1; color: var(--accent-text); }
th[data-sort]:hover::after  { opacity: 0.8; }

/* Row states */
tr.row-selected td { background: var(--accent-bg) !important; }
tr.row-loading td  { opacity: 0.5; pointer-events: none; }

/* Table toolbar */
.table-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  flex-wrap: wrap;
}
.table-toolbar-count {
  font-size: 12px;
  color: var(--muted);
  margin-left: auto;
  white-space: nowrap;
}

/* Compact table */
.table-compact th { padding: 8px 12px; }
.table-compact td { padding: 8px 12px; font-size: 12px; }

/* Zebra striping */
.table-striped tbody tr:nth-child(even) td { background: var(--bg-soft); }

/* ── 46. FORM ENHANCEMENTS ───────────────────────────────────────────── */
/* Input with icon */
.input-wrap {
  position: relative;
}
.input-wrap .input-icon {
  position: absolute;
  left: 12px; top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  font-size: 14px;
  pointer-events: none;
  display: flex;
  align-items: center;
}
.input-wrap input { padding-left: 36px; }
.input-wrap .input-icon-right {
  left: auto; right: 12px;
}
.input-wrap.has-right input { padding-right: 36px; }

/* Character count */
.input-counter {
  display: flex;
  justify-content: flex-end;
  font-size: 11px;
  color: var(--muted);
  margin-top: 3px;
}
.input-counter.near-limit { color: var(--amber-text); }
.input-counter.at-limit   { color: var(--red-text); }

/* Select custom */
.form-group select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2394a3b8' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}

/* ── 47. RESPONSIVE ADDITIONS ────────────────────────────────────────── */
@media (max-width: 640px) {
  .hide-mobile  { display: none !important; }
  .modal-lg, .modal-xl { max-width: 100%; }
  .drawer { width: 100%; }
  .tabs { gap: 0; }
  .tab  { padding: 10px 12px; font-size: 12px; }
  .pagination { gap: 2px; }
  .page-btn { min-width: 30px; height: 30px; font-size: 12px; }
  #mds-toast-region { bottom: 72px; right: 12px; width: calc(100vw - 24px); }
}
@media (min-width: 641px) {
  .hide-desktop { display: none !important; }
}
@media (max-width: 1024px) {
  .hide-tablet { display: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════
   48. AUTH LAYER — login / register / forgot password / OTP
   ──────────────────────────────────────────────────────────────────────
   Every platform's auth portal is THIS markup. The only difference
   permitted between platforms is data-platform on <html> and the strings
   in .auth-brand-title / .auth-brand-tagline / .auth-brand-features.
   No platform may invent its own login layout.
   ══════════════════════════════════════════════════════════════════════ */

/* --- 48.1 shell ---------------------------------------------------- */
.auth-body {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
}

.auth-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
}

/* --- 48.2 brand panel (left) --------------------------------------- */
.auth-brand {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 48px;
  background: linear-gradient(160deg, var(--brand) 0%, var(--brand-2) 55%, var(--accent-2) 210%);
  color: #f1f5f9;
  overflow: hidden;
}
.auth-brand::after {
  content: '';
  position: absolute;
  right: -140px;
  bottom: -140px;
  width: 420px;
  height: 420px;
  border-radius: 50%;
  background: var(--accent-glow);
  filter: blur(80px);
  pointer-events: none;
}
.auth-brand > * { position: relative; z-index: 1; }

.auth-brand-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: #fff;
}
.auth-brand-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-glow);
  flex-shrink: 0;
}

.auth-brand-body { max-width: 460px; }
.auth-brand-title {
  font-size: 34px;
  line-height: 1.15;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin-bottom: 14px;
  color: #fff;
}
.auth-brand-tagline {
  font-size: 15px;
  line-height: 1.6;
  color: #cbd5e1;
  margin-bottom: 30px;
}
.auth-brand-features {
  display: flex;
  flex-direction: column;
  gap: 13px;
  list-style: none;
}
.auth-brand-feature {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  font-size: 13.5px;
  color: #e2e8f0;
}
.auth-brand-feature-icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 11px;
  font-weight: 700;
  margin-top: 1px;
}
.auth-brand-footer {
  font-size: 12px;
  color: #94a3b8;
  line-height: 1.6;
}

/* --- 48.3 form panel (right) --------------------------------------- */
.auth-panel {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
  background: var(--bg);
  overflow-y: auto;
}
.auth-panel-top {
  position: absolute;
  top: 20px;
  right: 24px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.auth-card {
  width: 100%;
  max-width: 400px;
}
.auth-card-boxed {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: 32px;
}

.auth-logo-mobile {
  display: none;
  align-items: center;
  gap: 10px;
  font-size: 16px;
  font-weight: 800;
  color: var(--text);
  margin-bottom: 26px;
}

.auth-title {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  margin-bottom: 6px;
}
.auth-subtitle {
  font-size: 13.5px;
  color: var(--text-2);
  line-height: 1.6;
  margin-bottom: 26px;
}

/* --- 48.4 sign in / register tab switch ---------------------------- */
.auth-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
  margin-bottom: 24px;
}
.auth-tab {
  padding: 9px 12px;
  border: none;
  background: transparent;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  transition: all .18s ease;
}
.auth-tab:hover { color: var(--text); }
.auth-tab.active {
  background: var(--surface);
  color: var(--accent-text);
  box-shadow: var(--shadow-sm);
}

/* --- 48.5 form ----------------------------------------------------- */
.auth-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.auth-form .form-group { margin-bottom: 0; }

.auth-submit {
  width: 100%;
  justify-content: center;
  height: 44px;
  font-size: 14px;
  font-weight: 600;
}

.auth-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: -2px;
}
.auth-remember {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-2);
  user-select: none;
  cursor: pointer;
}
.auth-link {
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-text);
  transition: opacity .15s ease;
}
.auth-link:hover { opacity: .78; text-decoration: underline; }

.auth-alt {
  margin-top: 22px;
  text-align: center;
  font-size: 13px;
  color: var(--text-2);
}
.auth-alt a { color: var(--accent-text); font-weight: 600; }
.auth-alt a:hover { text-decoration: underline; }

/* --- 48.6 OAuth ---------------------------------------------------- */
.auth-oauth {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.btn-oauth {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 44px;
  padding: 0 16px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  transition: all .18s ease;
}
.btn-oauth:hover {
  background: var(--surface-2);
  border-color: var(--muted);
}
.btn-oauth:disabled { opacity: .5; cursor: not-allowed; }
.btn-oauth svg,
.btn-oauth img { width: 18px; height: 18px; flex-shrink: 0; }

/* Region gate: China deployments must hide Google OAuth (it does not
   resolve behind the GFW). Set data-region="cn" on <html>.               */
[data-region="cn"] .oauth-google { display: none !important; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 22px 0;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* --- 48.7 password field ------------------------------------------- */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 44px; }
.pw-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: none;
  background: transparent;
  border-radius: var(--r-sm);
  color: var(--muted);
  font-size: 14px;
  line-height: 1;
  transition: all .15s ease;
}
.pw-toggle:hover { color: var(--text); background: var(--surface-2); }

.pw-strength { margin-top: 9px; }
.pw-strength-track {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
}
.pw-strength-seg {
  height: 4px;
  border-radius: 999px;
  background: var(--border);
  transition: background .25s ease;
}
.pw-strength-label {
  margin-top: 7px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
}
.pw-strength[data-score="1"] .pw-strength-seg:nth-child(-n+1) { background: var(--red); }
.pw-strength[data-score="2"] .pw-strength-seg:nth-child(-n+2) { background: var(--amber); }
.pw-strength[data-score="3"] .pw-strength-seg:nth-child(-n+3) { background: var(--blue); }
.pw-strength[data-score="4"] .pw-strength-seg { background: var(--green); }
.pw-strength[data-score="1"] .pw-strength-label { color: var(--red-text); }
.pw-strength[data-score="2"] .pw-strength-label { color: var(--amber-text); }
.pw-strength[data-score="3"] .pw-strength-label { color: var(--blue-text); }
.pw-strength[data-score="4"] .pw-strength-label { color: var(--green-text); }

/* --- 48.8 legal consent -------------------------------------------- */
.auth-legal {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-2);
}
.auth-legal input { margin-top: 2px; flex-shrink: 0; }
.auth-legal a { color: var(--accent-text); font-weight: 600; }
.auth-legal a:hover { text-decoration: underline; }

/* --- 48.9 OTP / verification code ---------------------------------- */
.otp-group {
  display: flex;
  gap: 9px;
  justify-content: space-between;
}
.otp-input {
  flex: 1;
  min-width: 0;
  height: 54px;
  text-align: center;
  font-size: 21px;
  font-weight: 700;
  font-family: var(--font-mono);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  transition: all .15s ease;
}
.otp-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
.otp-resend {
  margin-top: 16px;
  text-align: center;
  font-size: 13px;
  color: var(--text-2);
}

/* --- 48.10 language switch (trilingual EN / BM / 中文) --------------- */
.lang-switch {
  display: inline-flex;
  padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.lang-switch-btn {
  padding: 5px 11px;
  border: none;
  background: transparent;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  transition: all .18s ease;
}
.lang-switch-btn:hover { color: var(--text); }
.lang-switch-btn.active {
  background: var(--surface);
  color: var(--accent-text);
  box-shadow: var(--shadow-sm);
}

.auth-footer {
  margin-top: 30px;
  text-align: center;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.7;
}

/* ══════════════════════════════════════════════════════════════════════
   49. SETTINGS LAYER
   ──────────────────────────────────────────────────────────────────────
   Every platform's settings page is THIS shell with THIS section set.
   Platform-specific settings are added as ADDITIONAL sections at the end
   of the nav — they never replace or reorder the mandatory ten.
   ══════════════════════════════════════════════════════════════════════ */

.settings-layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}

/* --- 49.1 settings nav --------------------------------------------- */
.settings-nav {
  position: sticky;
  top: calc(var(--topbar-h) + 20px);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.settings-nav-group-label {
  padding: 14px 12px 6px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}
.settings-nav-group-label:first-child { padding-top: 0; }

.settings-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: none;
  background: transparent;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-2);
  text-align: left;
  width: 100%;
  transition: all .16s ease;
}
.settings-nav-item:hover { background: var(--surface-2); color: var(--text); }
.settings-nav-item.active {
  background: var(--accent-bg);
  color: var(--accent-text);
  font-weight: 600;
}
.settings-nav-item-icon {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  font-size: 14px;
  opacity: .85;
}
.settings-nav-item-danger { color: var(--red-text); }
.settings-nav-item-danger:hover { background: var(--red-bg); color: var(--red-text); }

/* --- 49.2 settings content ----------------------------------------- */
.settings-content {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
}

.settings-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.settings-section-head {
  padding: 18px 22px;
  border-bottom: 1px solid var(--border);
}
.settings-section-title {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 8px;
}
.settings-section-desc {
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-2);
}
.settings-section-body { padding: 6px 22px; }
.settings-section-foot {
  padding: 14px 22px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

/* --- 49.3 settings row (label left, control right) ------------------ */
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
}
.settings-row:last-child { border-bottom: none; }
.settings-row-stacked { align-items: flex-start; flex-direction: column; gap: 10px; }

.settings-row-main { min-width: 0; flex: 1; }
.settings-row-label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 7px;
}
.settings-row-desc {
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-2);
}
.settings-row-control {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}
.settings-row-control .form-group { margin-bottom: 0; }
.settings-row-control input,
.settings-row-control select { min-width: 200px; }
.settings-row-stacked .settings-row-control { width: 100%; }
.settings-row-stacked .settings-row-control input,
.settings-row-stacked .settings-row-control select,
.settings-row-stacked .settings-row-control textarea { width: 100%; }

/* --- 49.4 avatar row ------------------------------------------------ */
.settings-avatar-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 0;
  border-bottom: 1px solid var(--border);
}
.settings-avatar-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* --- 49.5 choice cards (theme / language / density) ----------------- */
.settings-choice {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 10px;
  width: 100%;
}
.settings-choice-opt {
  position: relative;
  padding: 13px 12px;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  text-align: center;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-2);
  cursor: pointer;
  transition: all .18s ease;
}
.settings-choice-opt:hover { border-color: var(--border-2); color: var(--text); }
.settings-choice-opt.active {
  border-color: var(--accent);
  background: var(--accent-bg);
  color: var(--accent-text);
}
.settings-choice-opt input { position: absolute; opacity: 0; pointer-events: none; }
.settings-choice-swatch {
  display: block;
  height: 34px;
  border-radius: var(--r-sm);
  margin-bottom: 9px;
  border: 1px solid var(--border);
}
.settings-choice-swatch-light { background: linear-gradient(135deg, #ffffff 50%, #f0f2f5 50%); }
.settings-choice-swatch-dark  { background: linear-gradient(135deg, #1e293b 50%, #0f172a 50%); }
.settings-choice-swatch-auto  { background: linear-gradient(135deg, #ffffff 50%, #0f172a 50%); }

/* --- 49.6 secret / API key display ---------------------------------- */
.settings-key {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.settings-key-value {
  flex: 1;
  min-width: 0;
  padding: 9px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- 49.7 integration rows ------------------------------------------ */
.settings-integration {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 15px 0;
  border-bottom: 1px solid var(--border);
}
.settings-integration:last-child { border-bottom: none; }
.settings-integration-logo {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 17px;
}

/* --- 49.8 danger zone ----------------------------------------------- */
.settings-danger {
  border-color: var(--red);
  box-shadow: none;
}
.settings-danger .settings-section-head {
  background: var(--red-bg);
  border-bottom-color: var(--red);
}
.settings-danger .settings-section-title { color: var(--red-text); }

/* --- 49.9 sticky save bar -------------------------------------------- */
.settings-savebar {
  position: sticky;
  bottom: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
}
.settings-savebar-msg { font-size: 12.5px; color: var(--text-2); }
.settings-savebar[hidden] { display: none; }

/* ══════════════════════════════════════════════════════════════════════
   50. RESPONSIVE — AUTH + SETTINGS
   ══════════════════════════════════════════════════════════════════════ */

@media (max-width: 980px) {
  .auth-split { grid-template-columns: 1fr; }
  .auth-brand { display: none; }
  .auth-logo-mobile { display: flex; }
  .auth-panel { min-height: 100vh; min-height: 100dvh; align-items: flex-start; padding-top: 64px; }
}

@media (max-width: 640px) {
  .auth-panel { padding: 56px 18px 34px; }
  .auth-card-boxed { padding: 22px; border-radius: var(--r); }
  .auth-title { font-size: 21px; }
  .otp-input { height: 48px; font-size: 19px; }
  .auth-row { gap: 8px; }
}

@media (max-width: 1024px) {
  .settings-layout { grid-template-columns: 1fr; gap: 18px; }
  .settings-nav {
    position: static;
    flex-direction: row;
    gap: 6px;
    overflow-x: auto;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
    scrollbar-width: none;
  }
  .settings-nav::-webkit-scrollbar { display: none; }
  .settings-nav-group-label { display: none; }
  .settings-nav-item {
    width: auto;
    flex-shrink: 0;
    white-space: nowrap;
    border-radius: 999px;
    border: 1px solid var(--border);
  }
  .settings-nav-item.active { border-color: var(--accent); }
}

@media (max-width: 640px) {
  .settings-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
  .settings-row-control { width: 100%; }
  .settings-row-control input,
  .settings-row-control select { min-width: 0; width: 100%; }
  .settings-section-head,
  .settings-section-body,
  .settings-section-foot { padding-left: 16px; padding-right: 16px; }
  .settings-savebar { flex-direction: column; align-items: stretch; }
  .settings-savebar .btn { justify-content: center; }
}

/* Bottom nav is 64px tall on mobile — settings save bar must clear it. */
@media (max-width: 768px) {
  .settings-savebar { bottom: 72px; }
}

/* ══════════════════════════════════════════════════════════════════════
   51. DISPLAY TYPEFACE
   ──────────────────────────────────────────────────────────────────────
   Outfit is the display face; Inter is the reading face. Applied to
   headings and figures only — never to body copy, labels, table cells,
   form inputs or help text, where Inter reads better at small sizes.

   Before v2.0 this split existed only in M-EasyHalal and M-EasySchool
   (74 pages), applied per-page and undeclared in the design system.
   It is now a token, and it applies to all 9 platforms identically.
   ══════════════════════════════════════════════════════════════════════ */

.page-title,
.section-title,
.card-title,
.modal-title,
.topbar-title,
.sidebar-logo,
.stat-value,
.auth-brand-title,
.auth-title,
.settings-section-title,
.h-display {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
}

/* Opt out on a case-by-case basis if a heading must match body copy. */
.font-body { font-family: var(--font) !important; }

/* narrow numeric input — SST rate, percentages, quantities */
.w-narrow { min-width: 100px; max-width: 140px; }

/* ══════════════════════════════════════════════════════════════════════
   50. GAMIFICATION & MOTION LAYER
   ──────────────────────────────────────────────────────────────────────
   Progress rails, mission cards, score rings, milestone states, XP chrome
   and one entrance state. Every rule here is PLATFORM-NEUTRAL: colour comes
   from the six accent tokens, so ESG renders moss green and Commerce renders
   green and nothing in this section knows which platform it is on.

   There is deliberately no [data-platform="…"] selector below, and there
   must never be one. A platform opts in by USING these classes in its
   markup, which is how every other component in this file already works.
   Where a genuine opt-out is needed it gates on a non-platform axis —
   prefers-reduced-motion, or the data-motion attribute — which any platform
   may set and which therefore creates no asymmetry in the file.

   THREE RULES THIS SECTION IS BUILT AROUND:

   1. STATE IS NEVER COLOUR ALONE (§6). Every state class here has a matching
      text slot — .journey-node-label, .mission-meta, .milestone-badge's own
      content. A done node says "Done"; a mission at 3 of 8 says "3 of 8".
      A green ring that says nothing is not an accessible progress indicator.

   2. NOTHING LOOPS. No animation in this section repeats. The one rule that
      may loop indefinitely is a live progress indicator, and this section
      does not ship one — .xp-bar and .mission-progress are static widths
      that transition when the value changes.

   3. DURATIONS COME FROM TOKENS. A hardcoded 0.3s here is the same defect
      class as a hardcoded #16A34A. Every duration below is --motion-*, every
      easing is --ease-*, every radius is --r*, every colour is a token.
   ══════════════════════════════════════════════════════════════════════ */

/* --- 50.1 journey rail ---------------------------------------------- */
/* A vertical progress spine. The rail is a border on the container so the
   nodes can sit on top of it without a second positioned element.        */
.journey {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding-left: 28px;
  border-left: var(--rail-w) solid var(--border);
}
.journey-rail {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.journey-node {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.journey-node::before {
  content: '';
  position: absolute;
  left: calc(-28px - var(--rail-w) - (var(--node-size) / 2) + (var(--rail-w) / 2));
  top: 0;
  width: var(--node-size);
  height: var(--node-size);
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--border);
  transition: background var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out),
              box-shadow var(--motion-base) var(--ease-out);
}
.journey-node.is-done::before {
  background: var(--accent);
  border-color: var(--accent);
}
.journey-node.is-active::before {
  background: var(--accent-bg);
  border-color: var(--accent);
  box-shadow: var(--shadow-glow);
}
.journey-node.is-pending::before {
  background: var(--surface-2);
  border-color: var(--border-2);
}
/* The text half of every state. Present because colour alone is not a state. */
.journey-node-label {
  font-family: var(--font);
  font-weight: 600;
  color: var(--text);
}
.journey-node-meta {
  font-size: 12px;
  color: var(--text-2);
}

/* --- 50.2 mission card ---------------------------------------------- */
.mission-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 16px;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--motion-base) var(--ease-out),
              transform var(--motion-base) var(--ease-out);
}
.mission-card:hover {
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}
.mission-card.is-complete {
  border-color: var(--accent);
}
/* The command-centre variant — deliberately dark in both themes.
   --accent-contrast is the "text on a strong coloured surface" token and is
   #ffffff on every platform, so it is the right one here. The meta line dims
   with opacity rather than a second colour: a literal rgba() in a RULE is a
   hardcoded colour, and this section may not contain one. Token DEFINITIONS
   carry literals — that is what a token is — but rules read tokens.        */
.mission-card--deep {
  background: var(--surface-deep);
  border-color: transparent;
  color: var(--accent-contrast);
}
.mission-card--deep .mission-meta {
  color: var(--accent-contrast);
  opacity: 0.72;
}
.mission-title {
  font-family: var(--font-display);
  font-weight: 600;
  margin-bottom: 4px;
}
.mission-meta {
  font-size: 12px;
  color: var(--text-2);
}
.mission-progress {
  height: 6px;
  border-radius: var(--r-sm);
  background: var(--border);
  overflow: hidden;
  margin-top: 12px;
}
.mission-progress > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--accent);
  transition: width var(--motion-slow) var(--ease-out);
}

/* --- 50.3 score ring ------------------------------------------------- */
/* A conic-gradient driven by one custom property. No canvas, no library.
   Set --score (0-100) on the element; the number is ALSO rendered as text
   inside .score-ring-value, because a ring on its own states nothing.     */
.score-ring {
  --score: 0;
  position: relative;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background:
    conic-gradient(var(--accent) calc(var(--score) * 1%), var(--border) 0);
  transition: background var(--motion-slow) var(--ease-out);
}
.score-ring::before {
  content: '';
  position: absolute;
  inset: var(--ring-thickness);
  border-radius: 50%;
  background: var(--surface);
}
.score-ring-value {
  position: relative;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
}
.score-ring-label {
  position: relative;
  font-size: 11px;
  color: var(--text-2);
}

/* --- 50.4 XP bar and level chip -------------------------------------- */
.xp-bar {
  height: 10px;
  border-radius: var(--r-sm);
  background: var(--border);
  overflow: hidden;
}
.xp-bar > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--accent);
  transition: width var(--motion-slow) var(--ease-out);
}
.level-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px;
  border-radius: var(--r-xl);
  background: var(--accent-bg);
  color: var(--accent-text);
  border: 1px solid var(--accent);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
}

/* --- 50.5 milestone badge -------------------------------------------- */
/* Carries its own text by construction — it is a text element.           */
.milestone-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: var(--r-xl);
  background: var(--surface-2);
  color: var(--text-2);
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  transition: background var(--motion-base) var(--ease-out),
              color var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out);
}
/* WAS `background: var(--accent-light); color: var(--accent-contrast)`, which
   is #ffffff on a pale tint — 1.20:1 in light mode, on every platform, since
   §50 shipped in Run 50. Not "below AA": white on near-white, and unreadable.
   --accent-bg is the 8% tint the rest of the system uses behind accent text,
   and --accent-text is legible on it in both themes. The border keeps --accent
   so the badge still reads as earned at a glance. */
.milestone-badge.is-earned {
  background: var(--accent-bg);
  color: var(--accent-text);
  border-color: var(--accent);
}
.milestone-badge.is-locked {
  opacity: 0.6;
}

/* --- 50.6 glowing stat card ------------------------------------------ */
/* A MODIFIER ON .stat-card, which exists in §11. It is not .stat-tile--glow;
   .stat-tile does not exist anywhere in this file, and a modifier on a base
   class that is not there is a rule that never matches and never warns.    */
.stat-card--glow {
  box-shadow: var(--shadow-glow);
  border-color: var(--accent);
}

/* --- 50.7 reveal on scroll ------------------------------------------- */
/* THE RESTING STATE IS VISIBLE. The hidden state applies only under
   [data-reveal="on"], which JS sets on <html> AFTER it has confirmed it has
   an IntersectionObserver to turn it back on again.

   Authored this way round on purpose: if the script never runs, never loads,
   or throws, the content is simply visible. The opposite arrangement — hide
   in CSS, reveal in JS — makes the whole page depend on a script it has no
   way to check, and fails silently to exactly the users least able to report
   it. Reduced-motion handling lives in §41's existing media block.        */
.reveal { opacity: 1; }
[data-reveal="on"] .reveal {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--motion-slow) var(--ease-out),
              transform var(--motion-slow) var(--ease-out);
}
[data-reveal="on"] .reveal.is-visible {
  opacity: 1;
  transform: none;
}
/* An explicit, user-settable opt-out on a NON-PLATFORM axis. */
[data-motion="off"] .reveal,
[data-motion="off"] .reveal.is-visible {
  opacity: 1;
  transform: none;
  transition: none;
}

/* ═══════════════════════════════════════════════════════════════════════════
   52. COMPOSITION LAYER
   ───────────────────────────────────────────────────────────────────────────
   §50 gave this system its MOTION vocabulary. This gives it the COMPOSITION
   one. The gap between a product that reads as a school exercise and one that
   reads as infrastructure is not polish: it is that every page here is a
   one-column stack of full-width cards, and density is most of what
   "professional" means on a dashboard.

   NUMBERED 52, NOT 51. §51 is DISPLAY TYPEFACE and has been since v2.0, and
   this file already carries TWO sections numbered 50 — "RESPONSIVE — AUTH +
   SETTINGS" and "GAMIFICATION & MOTION LAYER", the second appended in Run 50
   without checking. Repeating that would make a section number useless as a
   reference for the third time.

   PLATFORM-NEUTRAL, like §50. Colour comes from the six accent tokens and the
   status palette, so ESG renders moss green and Campus renders crimson and
   nothing below knows which platform it is on. There is deliberately no
   [data-platform="…"] selector here and there must never be one.

   WHAT WAS NOT ADDED, BECAUSE IT ALREADY EXISTS. Checked before writing, which
   is the whole lesson of Run 53 finding a complete app shell this ecosystem
   had been hand-rolling past for months:

     a step rail across a panel header   .stepper / .step / .step-dot / .step-label
     a labelled progress bar             .progress / .progress-bar / .progress-labeled
     a static pill                       .tag / .tag-accent  (a LABEL, not a control)
     a metric card                       .stat-card / .stat-label / .stat-value / .stat-sub
     spacing, flex and text utilities    .gap-*, .flex-*, .items-*, .justify-*, .text-*

   Four of the components below compose those rather than restating them.
   ═══════════════════════════════════════════════════════════════════════════ */

/* --- 52.1 the twelve-column grid ------------------------------------------
   .grid ALREADY EXISTS as a bare `display: grid` utility and is in use, so it
   is not redefined here — a shipped utility that changes meaning under
   existing markup is the §1 hazard this file exists to prevent. The columned
   grid is its own name.

   Collapses to one column at the SAME ≤768px breakpoint the app shell uses.
   One breakpoint per system; a second one is a second layout to reason about. */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
}
.grid-12.grid--dense { gap: 10px; }

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

/* --- 52.2 the panel ------------------------------------------------------
   The numbered card chrome. This is the component that makes nine different
   cards read as one system rather than as nine cards.

   .panel is NOT .card with extras: a card is a container, a panel is a
   container that knows its own position in a sequence. Where there is no
   sequence, use .card. The step rail across a panel header is the EXISTING
   .stepper — it is not restated here. */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.panel-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}
.panel-index {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-text);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.panel-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.2px;
}
.panel-meta {
  margin-left: auto;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.panel-body { padding: 18px; }

/* The accent top-border the mock uses to separate a wall of panels. A 2px rule
   rather than a coloured header: it reads at a glance and costs no contrast. */
.panel--accent-top { border-top: 2px solid var(--accent); }

/* The command-centre variant, deliberately dark in BOTH themes — that is what
   --surface-deep is for, and §50 already establishes the pattern on
   .mission-card--deep. --accent-contrast is the "text on a strong coloured
   surface" token and is #ffffff on every platform, so it is the right one. */
.panel--deep {
  background: var(--surface-deep);
  border-color: transparent;
  color: var(--accent-contrast);
}
/* --accent-glow, not an rgba() literal. A literal in a RULE is a hardcoded
   colour and this section may not contain one — §50 solved the same problem on
   .mission-card--deep by reaching for opacity, and the glow token is the
   equivalent for a divider: 20% accent, which reads as a faint rule on a deep
   surface in either theme. */
.panel--deep .panel-head { border-bottom-color: var(--accent-glow); }
.panel--deep .panel-title { color: var(--accent-contrast); }
.panel--deep .panel-meta { color: var(--accent-contrast); opacity: 0.72; }

/* --- 52.3 filter chips ---------------------------------------------------
   .tag already exists and is a LABEL — it describes something. A chip is a
   CONTROL: it is pressed, it has an active state, and it carries a count. Two
   names because they are two jobs; they share a shape on purpose.

   THE ACTIVE STATE IS NOT COLOUR ALONE (§6): the active chip also carries a
   border, and the markup carries aria-pressed, which is what a screen reader
   and a monochrome display actually read. */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--r-xl);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-2);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out),
              color var(--motion-fast) var(--ease-out);
}
.chip:hover { background: var(--surface-2); }
.chip.is-active {
  background: var(--accent-bg);
  border-color: var(--accent);
  color: var(--accent-text);
}
.chip-count {
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
}
.chip:disabled,
.chip[aria-disabled="true"] { opacity: 0.5; cursor: default; }

/* --- 52.4 the checklist --------------------------------------------------
   Every item states its status in WORDS as well as by mark and colour. A
   checklist whose middle rows are decorative is a fallback wearing a nicer
   coat, so the three states are the three that exist and there is no fourth
   for "looks busy". */
.checklist {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.checklist-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--text);
}
.checklist-item + .checklist-item { border-top: 1px solid var(--border); }
.checklist-mark {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--border-2);
  display: grid;
  place-items: center;
  font-size: 10px;
  flex-shrink: 0;
  color: var(--muted);
  transition: background var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out);
}
.checklist-item.is-done .checklist-mark {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.checklist-item.is-active .checklist-mark {
  border-color: var(--accent);
  background: var(--accent-bg);
}
.checklist-item.is-pending { color: var(--text-2); }
.checklist-status {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.checklist-item.is-done .checklist-status { color: var(--accent-text); }

/* --- 52.5 the file row ---------------------------------------------------
   Icon, name, size, status. Deliberately not a table: a list of two to twenty
   files is a list, and a table of it has to invent column headings. */
.file-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 13px;
}
.file-row + .file-row { margin-top: 6px; }
.file-row-icon {
  width: 26px;
  height: 26px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  display: grid;
  place-items: center;
  font-size: 13px;
  flex-shrink: 0;
}
.file-row-main { flex: 1; min-width: 0; }
.file-row-name {
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.file-row-meta { font-size: 11px; color: var(--muted); margin-top: 1px; }
.file-row-status { flex-shrink: 0; }

/* --- 52.6 the metric row -------------------------------------------------
   Label, value, bar. COMPOSES .progress rather than restating a bar: there is
   one bar in this system and one place it is defined. */
.metric-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
}
.metric-row + .metric-row { border-top: 1px solid var(--border); }
.metric-row-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  min-width: 110px;
  flex-shrink: 0;
}
.metric-row .progress { flex: 1; }
.metric-row-value {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  min-width: 44px;
  text-align: right;
  flex-shrink: 0;
}

/* --- 52.7 the KPI tile ---------------------------------------------------
   The small "Strengths 12" counters. Smaller and quieter than .stat-card,
   which is a headline figure; these sit in a row of three or four beneath one. */
.kpi-tile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.kpi-tile-icon { font-size: 18px; flex-shrink: 0; opacity: 0.8; }
.kpi-tile-value {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 800;
  color: var(--text);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.kpi-tile-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  margin-top: 3px;
}

/* --- 52.8 the score ring, extended ---------------------------------------
   §50 ships .score-ring at one size with a value and a label. The mock needs
   a hero ring with a denominator, a caption and a band chip, and small inline
   rings for the sub-scores. Extended, never redefined: .score-ring keeps its
   96px default and everything below is a modifier or a new child.

   STILL NO CANVAS AND NO LIBRARY. The arc is the conic-gradient §50 already
   drives from --score, and the number is TEXT in the DOM — a ring that only
   reads correctly after a script has run shows a wrong number to a screen
   reader, to a no-JS visitor, and to anyone whose script was dropped. */
.score-ring--hero { width: 152px; height: 152px; }
.score-ring--hero .score-ring-value { font-size: 40px; }
.score-ring--hero .score-ring-label { font-size: 12px; }
.score-ring--inline { width: 64px; height: 64px; }
.score-ring--inline .score-ring-value { font-size: 16px; }
.score-ring--inline .score-ring-label { font-size: 9px; }

/* Value and denominator on one baseline: "78" large, "/100" quiet. */
.score-ring-figure {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 1px;
}
.score-ring-den {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}
/* Caption and band sit OUTSIDE the ring — inside it they fight the arc. */
.score-ring-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.score-ring-caption {
  font-size: 12px;
  color: var(--text-2);
  text-align: center;
  max-width: 200px;
}

/* --- 52.9 responsive -----------------------------------------------------
   ONE breakpoint, the same ≤768px the app shell already uses. */
@media (max-width: 768px) {
  .grid-12 { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .col-1, .col-2, .col-3, .col-4, .col-5, .col-6,
  .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 { grid-column: span 1; }
  .score-ring--hero { width: 120px; height: 120px; }
  .score-ring--hero .score-ring-value { font-size: 32px; }
  .metric-row { flex-wrap: wrap; }
  .metric-row-label { min-width: 0; }
  .panel-meta { display: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   53. STAT CAROUSEL
   ───────────────────────────────────────────────────────────────────────────
   A .stat-grid whose cards can be TAPPED OPEN. The tapped card becomes a hero
   above the grid; the rest shrink and reflow into a tighter row beneath it.

   ── Opt-in, and that is the whole compatibility story ──────────────────────

   A bare `.stat-grid` is untouched by every rule in this section. Nothing here
   applies until a wrapper carries `.stat-carousel`, so the ninety-odd stat
   grids across this ecosystem that have not adopted it render exactly as they
   did. §52's "a composition layer ADDS" rule, applied one section later.

   ── Where the motion is, and why none of it is here ────────────────────────

   The layout has two states and this file declares both. The MOVEMENT between
   them is a FLIP — measure, reflow, invert, play — and it is driven from
   stat-carousel.js with the Web Animations API, because a CSS transition
   cannot animate a grid reflow: the cards change parent, column and size at
   once, and there is no interpolable property between the two layouts.

   That is also why this section adds no `prefers-reduced-motion` block. §6a is
   explicit that there is exactly ONE in this file and a second would make the
   result depend on source order; the script reads the media query itself and
   skips the animation, so the layout change lands instantly and every resting
   state below is already the final one. Nothing here is revealed BY motion, so
   nothing here can be hidden by its absence.

   ── The tones ──────────────────────────────────────────────────────────────

   `data-tone` is declared at the top of this section rather than inside the
   carousel, because which colour a statistic carries is a property of the
   STATISTIC and not of the interaction. A page that never adopts the carousel
   can still tone its cards.

   Each tone spends three tokens that already exist and pairs them the way
   `.badge-*` already pairs them: the tint as the surface, the status colour as
   the rail, the -text variant as the figure. §6 forbids a status colour as
   TEXT, which is exactly why the rail takes `--amber` and the figure takes
   `--amber-text` rather than both taking one of them.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 53a. TONES — usable on any .stat-card, carousel or not ─────────────── */
.stat-card[data-tone] { background: var(--surface); }

.stat-card[data-tone="amber"]   { background: var(--amber-bg); }
.stat-card[data-tone="amber"]::before   { background: var(--amber); }
.stat-card[data-tone="amber"] .stat-value   { color: var(--amber-text); }

.stat-card[data-tone="purple"]  { background: var(--purple-bg); }
.stat-card[data-tone="purple"]::before  { background: var(--purple); }
.stat-card[data-tone="purple"] .stat-value  { color: var(--purple-text); }

.stat-card[data-tone="green"]   { background: var(--green-bg); }
.stat-card[data-tone="green"]::before   { background: var(--green); }
.stat-card[data-tone="green"] .stat-value   { color: var(--green-text); }

.stat-card[data-tone="blue"]    { background: var(--blue-bg); }
.stat-card[data-tone="blue"]::before    { background: var(--blue); }
.stat-card[data-tone="blue"] .stat-value    { color: var(--blue-text); }

.stat-card[data-tone="red"]     { background: var(--red-bg); }
.stat-card[data-tone="red"]::before     { background: var(--red); }
.stat-card[data-tone="red"] .stat-value     { color: var(--red-text); }

/* The platform's own colour. This is the one tone that differs between
   platforms, and it differs by exactly the amount THE ONE RULE permits. */
.stat-card[data-tone="accent"]  { background: var(--accent-bg); }
.stat-card[data-tone="accent"]::before  { background: var(--accent); }
.stat-card[data-tone="accent"] .stat-value  { color: var(--accent-text); }

/* No tint and no colour on the figure — for a total, which is not a status.
   The rail stays the platform accent, as every untoned stat card's already is. */
.stat-card[data-tone="neutral"] { background: var(--surface); }
.stat-card[data-tone="neutral"] .stat-value { color: var(--text); }

/* NOT MEASURED, which is not the same as zero and must not look like it.
   §7's checklist requires empty states to separate uninstrumented from
   instrumented-but-empty from a real zero, and a tile has only its figure and
   its colour to say which it is. The figure goes to --muted so a dash reads as
   "no reading" rather than as a number that happens to be small, and the rail
   goes with it so the card does not claim the platform accent for a statistic
   nobody is collecting. --muted is 4.6:1 on --surface in both themes, measured
   in §6 — quiet, and still readable. */
.stat-card[data-tone="muted"] { background: var(--surface); }
.stat-card[data-tone="muted"]::before { background: var(--border-2); }
.stat-card[data-tone="muted"] .stat-value { color: var(--muted); }

/* ── 53b. THE CAROUSEL ──────────────────────────────────────────────────── */
.stat-carousel { display: block; margin-bottom: 20px; }

/* The grid inside a carousel drops §11's auto-fit for a FIXED two columns.
   auto-fit is right for a row of read-only tiles and wrong here: the count of
   cards changes when one is promoted to hero, and auto-fit would re-solve the
   track count on its own, mid-flight, fighting the measured FLIP. */
.stat-carousel .stat-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 0;
}
.stat-carousel.is-expanded .stat-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* §11 sets `transition: all` on .stat-card, which would fight every transform
   the FLIP writes and then animate it back on release. Narrowed to the one
   property that should still ease. */
.stat-carousel .stat-card {
  transition: box-shadow var(--motion-base) var(--ease-out);
}

/* The slot the hero moves INTO. Empty and out of flow until then, so a
   collapsed carousel occupies exactly the height its grid does. */
.stat-carousel-hero { display: none; margin-bottom: 14px; }
.stat-carousel.is-expanded .stat-carousel-hero { display: block; }

/* ── 53c. THE CARD AS A CONTROL ─────────────────────────────────────────── */
/* The whole card is the target, and it is a real <button> rather than a div
   with a click handler: focus, Enter, Space, the accessibility tree and the
   focus ring all come free and correct. The button carries the label and the
   value; the ::after stretches its hit area over the entire card so the
   padding is clickable too. */
.stat-cc-btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.stat-cc-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}
/* §11 styles .stat-label / .stat-value / .stat-sub without ever setting a
   display, because everywhere else they are divs and a div is already a block.
   A <button> may only contain PHRASING content, so inside the trigger they are
   spans — and a span with a margin-bottom and no display renders inline, which
   silently collapses the whole card onto one line. Declared here rather than
   by using divs inside a button and relying on browsers to forgive it. */
.stat-cc-btn .stat-label,
.stat-cc-btn .stat-value,
.stat-cc-btn .stat-sub { display: block; }
/* NO FOCUS RULE HERE ON PURPOSE. §41 already draws a 2px --accent outline at
   2px offset on every :focus-visible, and §6 says never remove it. The button
   fills the card's CONTENT box, so that ring lands inside the card's 18px of
   padding and is not clipped by §11's `overflow: hidden` — which is exactly
   what a hand-rolled replacement here would have had to work around. Checked
   by rendering, not assumed: an earlier draft removed the outline and rebuilt
   it with :has(), which would have left no ring at all on a browser without
   :has() support. */

/* ── 53d. THE HERO ──────────────────────────────────────────────────────── */
.stat-card.is-hero {
  padding: 24px 26px;
  box-shadow: var(--shadow-lg);
  border-radius: var(--r-lg);
}
/* NO RULE ON ::before HERE, AND THAT IS THE POINT. §11 draws the tone rail as
   a 3px bar across the TOP of the card; a platform layer is free to move it,
   and one already has — M-EasyPOS re-inets it to a full-height rail down the
   LEFT edge. A hero rule thickening it with `height: 6px` therefore did not
   thicken anything: it overrode that platform's `height: auto` and collapsed a
   full-height left rail to a 6px stub, so the promoted card was the one card on
   the page with no tone rail at all. Caught by looking at the rendered hero.

   The rail's geometry belongs to whoever last set it. §53 only decides which
   COLOUR it takes, in 53a, which works whichever edge it is on. */
.stat-card.is-hero .stat-label { font-size: 12px; margin-bottom: 10px; }
.stat-card.is-hero .stat-value { font-size: 46px; letter-spacing: -1.5px; }

/* Actions sit ABOVE the stretched hit area, so pressing one does not also
   collapse the card underneath it. */
.stat-cc-actions {
  position: relative;
  z-index: 2;
  display: none;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
  flex-wrap: wrap;
}
.stat-card.is-hero .stat-cc-actions { display: flex; }

/* Close, top-right of the hero. Its own control rather than relying on the
   card's own toggle, because "press the thing again" is a discoverable gesture
   and not an obvious one. */
.stat-cc-close {
  position: absolute;
  top: 14px; right: 14px;
  z-index: 2;
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: none;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  transition: color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
.stat-card.is-hero .stat-cc-close { display: flex; }
.stat-cc-close:hover { color: var(--text); border-color: var(--border); }

/* ── 53e. THE SHRUNKEN REST ─────────────────────────────────────────────── */
/* Denser, not merely narrower. Three columns of the resting card's padding and
   type would overflow every one of them on a phone. */
.stat-carousel.is-expanded .stat-grid .stat-card { padding: 12px 13px; }
.stat-carousel.is-expanded .stat-grid .stat-label {
  font-size: 10px;
  letter-spacing: 0.4px;
  margin-bottom: 4px;
}
.stat-carousel.is-expanded .stat-grid .stat-value {
  font-size: 18px;
  letter-spacing: -0.3px;
}

/* ── 53f. THE PHONE ─────────────────────────────────────────────────────── */
/* The same 768px the app shell already uses — §52 established that this file
   gets one breakpoint and it is that one. Two columns collapse to two, and the
   expanded row stays at three: three shrunken tiles fit 320px at the type size
   above, and dropping to two would reflow the grid a second time mid-gesture. */
@media (max-width: 768px) {
  .stat-carousel .stat-grid { gap: 10px; }
  .stat-card.is-hero { padding: 20px; }
  .stat-card.is-hero .stat-value { font-size: 36px; }
  .stat-carousel.is-expanded .stat-grid .stat-value { font-size: 15px; }
  .stat-cc-actions { margin-top: 14px; }
}
