/* ============================================
   DENTAL CLINIC MANAGEMENT - GLOBAL STYLES
   RTL Arabic interface with 3 theme options
   ============================================ */

/* ============================================================
   Modirify Design Tokens (Phase 3 U-1)
   ============================================================
   Foundation tokens — type scale, z-index scale, focus rings,
   glass surfaces, hover-lift. Loaded BEFORE
   the main styles.css via an @import at the top of styles.css so
   every theme block and component class can reference these
   values without worrying about cascade order.

   What IS in this file:
     §1 Type scale       — 6 steps, Hybrid Linear+Stripe (V-1/2, ADR-048)
     §2 Z-index scale    — 8 semantic layers (replaces 1..3000 chaos)
     §3 Focus tokens     — unified accent ring (2 strengths)
     §4 Glass tokens     — single source of truth, auto-dark (A1)
     §5 Hover-lift       — only 3 allowed values (-1 / -2 / -3)
     §6 Density          — placeholder for U-10 (decision C2)
     §7 Shimmer + tint   — V-1/5 (ADR-048) accent-tinted skeleton +
                           Stripe hero-card soft gradient base

   What is NOT here (lives in styles.css, intentionally untouched):
     • --space-1..12, --duration-*, --ease-*          (already tokenized)
     • --bg-*, --text-primary/-secondary/-tertiary,
       --border-*, --accent-*                         (theme palette)
     • --success/-soft, --warning/-soft, ...          (status palette)
     • --radius-*, --shadow-*                         (per-theme already)

   See DECISIONS.md ADR-017 for the U-1 design rationale and
   ADR-048 for the V-1/2 Hybrid 6-size pivot.
============================================================ */

:root {
  /* ──────────────────────────────────────────────────────────
     §1 Type scale (V-1/2, ADR-048) — Hybrid 6 sizes
     Linear-clarity's 5 sizes (11/13/15/22/36) + Stripe-density's
     --text-base (14px) for data-dense surfaces (Billing / Finance
     / Analytics tables). Replaces the U-1 10-token scale.

     Use guide:
       --text-xs   (11px)  chips, badges, metadata
       --text-sm   (13px)  body — default
       --text-base (14px)  data-dense ONLY (tables in Billing/Finance/Analytics)
       --text-md      (15px)  card titles, primary text, buttons, inputs
       --text-title   (22px)  page titles, KPI value (compact)
       --text-display (36px)  hero numbers (dashboard greeting, metric tile value)

     Sizing W0 rename (2026-07-08): the top two scale steps were named
     --text-lg / --text-xl, which COLLIDE by name with the .text-lg (16px)
     / .text-xl (20px) utility classes in styles.css — same name, different
     value, a documented footgun. Renamed the TOKENS to --text-title /
     --text-display (semantic, no numeric suffix) so a token can never be
     confused with a utility class again. Values unchanged (22 / 36).

     Dropped vs U-1: --text-2xs (10px) absorbed into --text-xs; intermediate
     --text-lg (16) / --text-xl (18) / --text-2xl (22) / --text-3xl (28) /
     --text-4xl (36) compressed into the 6-step ladder. The single styles.css
     consumer of the old --text-xl (.empty-state.v2.is-lg title, was 18px)
     is remapped to the new --text-title (22px) — semantic match for a title.

     Line-heights stay on the existing 4-step --leading-* scale
     (tight/snug/normal/relaxed = 1.2/1.35/1.5/1.7) — paired by
     screen-author at touch point, not auto-paired here.
     ────────────────────────────────────────────────────────── */
  /* ADR-291 W2: two steps the scale was MISSING and the app leaned on anyway.
     12px is the single most-used size in the product (112 declarations) and had
     no token at all; 10px carries counters and table micro-labels (38). A scale
     that omits the value its own app reaches for most is a scale nobody can
     follow — so the scale was corrected rather than 150 places being dragged
     onto a step that never fit them. Both are pure renames: not one pixel moved.
     Deliberately NOT added: 12.5 / 11.5 / 10.5 / 13.5. Those are eyeballed, not
     chosen, and they snap onto whole steps in a separate measured pass. */
  --text-micro: 10px;
  --text-xs:   11px;
  --text-meta: 12px;
  --text-sm:   13px;
  --text-base: 14px;
  --text-md:      15px;
  --text-title:   22px;
  --text-display: 36px;

  /* Line-heights paired with the type scale */
  --leading-tight:   1.2;   /* hero titles, large numbers */
  --leading-snug:    1.35;  /* card titles, section heads */
  --leading-normal:  1.5;   /* body text */
  --leading-relaxed: 1.7;   /* long-form descriptions, modal sub */

  /* ──────────────────────────────────────────────────────────
     §2 Z-index scale — 8 semantic layers
     Order top-to-bottom: tooltips > toasts > drawers > modals
     > dropdowns > topbar > sticky > base. The boot splash uses
     --z-max so app overlays never sit on top of it.
     ────────────────────────────────────────────────────────── */
  --z-base:           1;
  --z-sticky:         10;
  --z-topbar:         50;
  --z-dropdown:       100;   /* notifications, theme picker, date filter */
  --z-modal-backdrop: 200;
  --z-modal:          210;
  --z-drawer:         300;
  --z-toast:          500;
  --z-tooltip:        700;
  --z-max:            999;   /* boot splash — never go past */

  /* ──────────────────────────────────────────────────────────
     §3 Focus tokens — unified accent ring
     Use --ring-shadow for buttons / interactive cards (3px, 35%).
     Use --ring-shadow-soft for inputs / inline targets (3px, 22%).
     Replaces five inline copies of
     "0 0 0 3px color-mix(... accent 22%, transparent)".
     ────────────────────────────────────────────────────────── */
  --ring-width:       3px;
  --ring-color:       color-mix(in srgb, var(--accent) 35%, transparent);
  --ring-color-soft:  color-mix(in srgb, var(--accent) 22%, transparent);
  --ring-shadow:      0 0 0 var(--ring-width) var(--ring-color);
  --ring-shadow-soft: 0 0 0 var(--ring-width) var(--ring-color-soft);

  /* ──────────────────────────────────────────────────────────
     §4 Glass tokens (A1) — single source of truth
     Light-themed surfaces use --glass-bg / --glass-border /
     --glass-shadow. Dark themes (luxe, midnight) override these
     in their own theme block via auto-switch below.
     Replaces the four-way duplication of
     ".modal", ".gm-box", ".accounting-modal", ".mk2-drawer".
     ────────────────────────────────────────────────────────── */
  --glass-bg:        rgba(255, 255, 255, 0.88);
  --glass-blur:      24px;
  --glass-blur-sm:   10px;
  --glass-border:    rgba(255, 255, 255, 0.6);
  --glass-shadow:
    0 24px 60px rgba(0, 0, 0, 0.14),
    0 4px 16px rgba(0, 0, 0, 0.08);

  /* Dark-theme variants — applied below for luxe + midnight */
  --glass-bg-dark:     rgba(13, 18, 38, 0.87);
  --glass-border-dark: rgba(255, 255, 255, 0.08);
  --glass-shadow-dark:
    0 24px 60px rgba(0, 0, 0, 0.45),
    0 4px 16px rgba(0, 0, 0, 0.35);

  /* ──────────────────────────────────────────────────────────
     §5 Hover-lift — only 3 allowed values
     --lift-1: subtle (chips, list rows, table rows).
     --lift-2: standard (cards, KPI tiles, nav items).
     --lift-3: prominent (metric tiles, feature cards).
     Magic translateY values outside these three are a smell.
     ────────────────────────────────────────────────────────── */
  --lift-1: -1px;
  --lift-2: -2px;
  --lift-3: -3px;

  /* §6 Density (C2) — REMOVED 2026-07-28. `--density-scale` sat at 1 with no
     consumer for the whole life of the codebase, waiting on a "U-10" that never
     shipped, while the toggle it was reserved for stamped `data-density` on
     <html> for zero CSS rules to read. A placeholder that outlives its plan is
     just a claim the app makes about itself and does not keep. */

  /* ──────────────────────────────────────────────────────────
     §7 Shimmer + tint (V-1/5, ADR-048) — Hybrid Stripe layer
     --shimmer-base       — base color of the skeleton gradient
                            (matches loading-surface background).
     --shimmer-highlight  — accent-tinted highlight that sweeps
                            across. 7% accent mixed with bg-hover —
                            subtle teal pulse on Clean, theme-aware
                            on every other theme (so dark themes
                            stay dark-and-accent-flavored, not
                            light-flashy).
     --shimmer-duration   — animation duration. 1.6s = slightly
                            slower than the legacy 1.4s for a more
                            "premium" rhythm (Vercel/Stripe pattern).
     --tint-hero-strength — opacity of the soft gradient overlay
                            on hero cards (Stripe's 4% sweep from
                            corner). Tokenized so V-1/3 dark themes
                            can override (dark surfaces need a
                            stronger tint to be visible).
     ────────────────────────────────────────────────────────── */
  --shimmer-base:       var(--bg-subtle);
  --shimmer-highlight:  color-mix(in srgb, var(--accent) 7%, var(--bg-hover));
  --shimmer-duration:   1.6s;
  --tint-hero-strength: 4%;

  /* ──────────────────────────────────────────────────────────
     §8 Logo sizes (V-Logo, ADR-070) — unified scale
     Source of truth lives in app/src/ui/logo.jsx (LOGO_SIZES);
     these tokens mirror the values for any CSS-only consumer
     (transitions, breakpoint overrides) without re-importing.
     ────────────────────────────────────────────────────────── */
  --logo-xs:  80px;   /* sidebar mini / compact strips */
  --logo-sm:  140px;  /* brand strip on verify / celebration cards */
  --logo-md:  200px;  /* onboarding welcome step */
  --logo-lg:  220px;  /* sidebar standard */
  --logo-xl:  280px;  /* login portal hero */
  --logo-transition: width 320ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 220ms ease;

  /* ──────────────────────────────────────────────────────────
     §9 Pre-auth palette (ADR-086) — single source of truth for
     the eight pre-auth surfaces (boot splash, landing nav, login
     portal, register, forgot, check-email, verify celebration,
     onboarding wizard). Each surface uses these tokens instead of
     hardcoded teal hexes so future theme variants (luxe, midnight
     etc.) can re-skin pre-auth without a find-and-replace pass.

     The values mirror the landing's --lp-accent + --lp-accent-dark
     + --lp-success (kept as local aliases on .lp-shell) and
     ADR-084 unified the rest of pre-auth onto this triplet. Any
     new pre-auth element MUST use these tokens; the smoke check
     `pre-auth-banned-hex` enforces it.
     ────────────────────────────────────────────────────────── */
  --auth-primary:      #0ea5b7;  /* primary brand teal             */
  --auth-primary-dark: #0a8a99;  /* dark teal — links, badges, text */
  --auth-success:      #22c55e;  /* completion / "ready" states     */
  --auth-primary-soft:  rgba(14, 165, 183, 0.10);  /* card tints */
  --auth-primary-tint:  rgba(14, 165, 183, 0.16);  /* hovers, borders */
  --auth-success-soft:  rgba(34, 197, 94, 0.10);   /* done states */

  /* ──────────────────────────────────────────────────────────
     §10 Responsive Foundation (Phase 1.5 — 2026-05-26)
     ────────────────────────────────────────────────────────── */
  /* Canonical breakpoints. These values are also referenced in
     @media queries throughout the codebase as raw px (CSS variables
     can't appear inside @media). Source of truth lives here so
     future audits have ONE place to read them.
       Phone landscape and up     ≥ 480px (30em)
       Tablet portrait and up     ≥ 768px (48em)
       Laptop and up              ≥ 1024px (64em)
       Desktop and up             ≥ 1280px (80em)
     Mobile-first convention: write CSS for phones first, then
     layer up with @media (min-width: ...). Avoid the inverted
     @media (max-width: ...) pattern outside transitional patches.
     ────────────────────────────────────────────────────────── */
  --bp-phone:   30em;   /* 480px  — phone landscape + up */
  --bp-tablet:  48em;   /* 768px  — tablet portrait + up */
  --bp-laptop:  64em;   /* 1024px — laptop + up          */
  --bp-desktop: 80em;   /* 1280px — desktop + up         */

  /* Fluid spacing — clamp(min, fluid-via-vw, max).
     The middle term scales with the viewport width so spacing
     grows smoothly between breakpoints instead of jumping.
     Used by the Layout Primitives (.l-stack / .l-cluster / etc).
     Numbers in comments are rem→px @ 16px root font.
     ────────────────────────────────────────────────────────── */
  --space-fluid-3xs: clamp(0.125rem, 0.25vw + 0.0625rem, 0.25rem); /*  2 →  4 */
  --space-fluid-2xs: clamp(0.25rem,  0.5vw  + 0.125rem,  0.5rem);  /*  4 →  8 */
  --space-fluid-xs:  clamp(0.5rem,   1vw    + 0.25rem,   0.75rem); /*  8 → 12 */
  --space-fluid-sm:  clamp(0.75rem,  1.5vw  + 0.375rem,  1rem);    /* 12 → 16 */
  --space-fluid-md:  clamp(1rem,     2vw    + 0.5rem,    1.5rem);  /* 16 → 24 */
  --space-fluid-lg:  clamp(1.5rem,   3vw    + 0.75rem,   2rem);    /* 24 → 32 */
  --space-fluid-xl:  clamp(2rem,     4vw    + 1rem,      3rem);    /* 32 → 48 */
  --space-fluid-2xl: clamp(2.5rem,   5vw    + 1.25rem,   4rem);    /* 40 → 64 */

  /* Fluid typography — for hero titles and large headlines that
     should breathe on desktop but stay legible on phones. Body
     text continues to use the fixed scale (§1) because consistent
     sizes help dense surfaces (tables) stay scannable. Use these
     ONLY for headlines that look awkward when they don't scale
     (e.g., the hero name "أحمد محمد" at 36px is too big on a 320px
     phone).
     ────────────────────────────────────────────────────────── */
  --fs-fluid-title: clamp(1.125rem, 1vw + 0.875rem, 1.375rem); /* 18 → 22 */
  --fs-fluid-h2:    clamp(1.25rem,  2vw + 0.75rem,  1.75rem);  /* 20 → 28 */
  --fs-fluid-hero:  clamp(1.5rem,   3vw + 0.75rem,  2.25rem);  /* 24 → 36 */

  /* Container size tokens — used by Layout Primitives to decide
     when to "switch" from horizontal to vertical layouts. These
     are intentionally LARGER than viewport breakpoints because
     they're container-aware, not viewport-aware.
     ────────────────────────────────────────────────────────── */
  --container-narrow: 30rem;  /* 480px — switcher / sidebar threshold */
  --container-prose:  60ch;   /* readable line length                 */
  --container-wide:   80rem;  /* 1280px — max page width              */

  /* ──────────────────────────────────────────────────────────
     §11 Premium modal shell tokens (V-Polish, 2026-05-27)
     Opt-in `<ResponsiveModal className="rm-card--premium">`
     reads these for the "Ctrl+K-like" feel: larger radius,
     deeper float shadow, more breathing padding, and a smooth
     input-focus glow scoped to inside the premium card.
     Adding `.rm-card--premium` to a modal pulls all of these
     in at once without changing any other modal. The base
     modal tokens stay untouched so legacy modals look the same.
     ────────────────────────────────────────────────────────── */
  --radius-modal-premium:    24px;   /* base modal-box is ~14-16px */
  --shadow-modal-premium:
    0 24px 60px rgba(0, 0, 0, 0.18),
    0 4px  16px rgba(0, 0, 0, 0.10);
  --pad-modal-premium:       28px;   /* base padding is 16-20px    */
  --pad-modal-premium-sm:    20px;   /* phone-tight padding        */
  --input-focus-glow-premium:
    0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);

  /* ──────────────────────────────────────────────────────────
     §12 Sizing System (PROJECT_SIZING.md Wave 0, 2026-07-08)
     Dimension tokens as ONE source of truth. The shared primitives
     ADOPT them during the presentation waves (UNIFICATION 8-10) - so
     these are DEFINED now (mostly additive, no render change) EXCEPT
     the two fixes below. FIXES --radius-md + --shadow-md, which were
     used-but-undefined and silently rendered as no-radius / no-shadow.
     ────────────────────────────────────────────────────────── */
  /* Control / hit-target heights. Owner keeps comfortable controls (his
     Claude-Design mockup used 44-46px) so these stay generous vs Shopify's 32. */
  --control-h-sm: 32px;  --control-h: 40px;  --control-h-lg: 44px;
  --field-h: 40px;  --table-row-h: 44px;  --table-head-h: 38px;  --touch-target: 44px;
  /* Icon glyph + avatar scales (fold ~620 hardcoded icon sizes + 5 avatar sizes) */
  --icon-xs: 12px;  --icon-sm: 14px;  --icon-md: 16px;  --icon-lg: 20px;  --icon-xl: 24px;
  --avatar-xs: 24px;  --avatar-sm: 32px;  --avatar-md: 40px;  --avatar-lg: 48px;  --avatar-xl: 64px;
  /* Radius: define the MISSING --radius-md (structural, kept global). The full
     global-radius consolidation + tighten (8/12/16) is a later Wave 0 step. */
  --radius-md: 8px;  --radius-xs: 4px;  --radius-pill: 9999px;  --radius-full: 50%;
  /* Elevation: define the MISSING --shadow-md (alias the theme-tinted --shadow so
     dark themes stay correct) + a blur scale. */
  --shadow-md: var(--shadow);
  --blur-sm: 10px;  --blur-md: 16px;  --blur-lg: 24px;
  /* Layout widths (named the current dimensions so they centralize; the tighten
     to 232/52 is an adoption-time decision). Modal ladder resolves the 760-vs-800
     conflict. Defined now, adopted by the shell/modal primitives later. */
  --sidebar-width: 260px;  --topbar-height: 64px;  --content-max: 1200px;
  --modal-sm: 400px;  --modal-md: 560px;  --modal-lg: 720px;  --modal-xl: 1040px;
  --drawer-width: 360px;
  /* Letter-spacing scale (folds ~28 ad-hoc px/em values). */
  --tracking-tight: -0.01em;  --tracking-normal: 0;  --tracking-wide: 0.04em;
}

/* Dark themes — deeper shadow so the premium card still reads as
   floating against the dark surface. ADR-205: keyed on data-theme-mode so
   EVERY dark theme (luxe/midnight/obsidian/indigo) inherits it, not just
   the two original ones. */

[data-theme-mode="dark"] {
  --shadow-modal-premium:
    0 24px 60px rgba(0, 0, 0, 0.55),
    0 4px  16px rgba(0, 0, 0, 0.35);
}

/* Auto-switch glass tokens for ALL dark themes (A1 fallback). ADR-205:
   was luxe/midnight only -> obsidian + indigo modals + the sticky
   .breadcrumbs strip rendered with the LIGHT glass (white). Keyed on
   data-theme-mode now; per-theme --glass-bg-dark (set in styles.css theme
   blocks) tints the glass to match each surface (navy / graphite / indigo). */

[data-theme-mode="dark"] {
  --glass-bg:     var(--glass-bg-dark);
  --glass-border: var(--glass-border-dark);
  --glass-shadow: var(--glass-shadow-dark);
}

/* Sizing W0: the UI font (Cairo) now loads once via the index.html <link> at the
   trimmed 400-700 weights. Dropped the duplicate render-blocking @import + the
   heavy 800/900 Cairo weights (those were the bulk, not the font itself). */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  width: 100%;
  /* 100dvh (not 100%) so html/body match #root's 100dvh exactly on mobile
     (dynamic viewport) -- no clipping of the fixed shell. overflow:hidden: the
     page/body NEVER scrolls; scrolling lives in #root / .main-content. */
  height: 100dvh;
  overflow: hidden;
  font-family: var(--font-sans, 'IBM Plex Sans Arabic', system-ui, sans-serif);
  direction: rtl;
  text-align: start;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Sizing W0: no faux-bold. Weights >700 (the ~344 hardcoded 800/900 sites) now
     resolve to the REAL IBM Plex 700 glyph instead of a synthesized heavier one. */
  font-synthesis: none;
}

/* Sizing W0: base body text = 14px (B2B density, down from the 16px browser
   default). Scoped to body (NOT html) so rem-based tokens/containers keep their
   16px root and don't rescale. */

body { font-size: var(--text-base); }

button, input, select, textarea {
  font-family: inherit;
  direction: rtl;
}

/* ============================================
   THEME 1: Clean Medical (Default)
   Soft white + medical teal/cyan
   ============================================ */

/* Unify W1 (2026-07-07): the --brand family was never defined anywhere, so
   every var(--brand, #0891b2) in the AI-insight / AI-watchers CSS fell back to
   a FIXED teal hex that ignored the active theme. Alias it GLOBALLY (on :root,
   OUTSIDE any [data-theme] block) to the themed --accent tokens. Custom props
   resolve at use-time on the html element, so --brand tracks whichever theme's
   --accent is active. MUST stay global: scoping it to one theme block breaks
   --brand under every other theme (it resolves to an invalid/black value). */

:root {
  /* Sizing W0: ONE font source of truth. Owner kept Cairo (familiar) but capped
     at weight 700 - the "bulky" feel was the 800/900 weights + 16px base, not the
     font. Swap this single token to go language-aware later (i18n). */
  --font-sans: 'Cairo', system-ui, 'Segoe UI', sans-serif;
  --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', Consolas, monospace;
  --brand: var(--accent);
  --brand-soft: var(--accent-soft);
  --brand-soft-border: color-mix(in srgb, var(--accent) 18%, transparent);
}

/* i18n (PROJECT_SIZING W0): --font-sans is language-swappable from ONE place.
   Arabic = Cairo (covers Arabic + Latin). Any non-Arabic language falls to a
   native Latin system stack - fast, no extra webfont, covers every script. The
   app already stamps <html lang> on language switch (App.jsx + i18n.js). */

html:not([lang="ar"]) {
  --font-sans: system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Mobile (PROJECT_SIZING W0): inputs >= 16px on phones so iOS Safari never
   zoom-jumps on focus (ANY sub-16px input triggers it). Desktop keeps its size. */

@media (max-width: 768px) {
  .input, .select, .textarea, input, select, textarea { font-size: 16px; }
}

[data-theme="clean"] {
  --bg: #f4f7fa;
  --bg-elevated: #ffffff;
  --bg-sidebar: #ffffff;
  --bg-card: #ffffff;
  --bg-hover: #f0f5f9;
  --bg-subtle: #eef3f8;

  --text-primary: #0f1f2e;
  --text-secondary: #4a5b6d;
  --text-tertiary: #8a98a8;
  --text-muted: #b5bfc9;

  --border: #e4ebf2;
  --border-strong: #d0dae4;

  --accent: #0891b2;           /* medical teal */
  --accent-hover: #0e7490;
  --accent-soft: #ecfeff;
  --accent-text: #155e75;

  --success: #059669;
  --success-soft: #d1fae5;
  --warning: #d97706;
  --warning-soft: #fef3c7;
  --danger: #dc2626;
  --danger-soft: #fee2e2;
  --info: #2563eb;
  --info-soft: #dbeafe;
  --purple: #7c3aed;
  --purple-soft: #ede9fe;

  --shadow-sm: 0 1px 2px rgba(15, 31, 46, 0.04);
  --shadow: 0 2px 8px rgba(15, 31, 46, 0.06), 0 1px 2px rgba(15, 31, 46, 0.04);
  --shadow-lg: 0 12px 32px rgba(15, 31, 46, 0.10), 0 2px 8px rgba(15, 31, 46, 0.06);

  --radius-sm: 6px;
  --radius: 12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 2: Luxe Dark
   Deep navy + gold/violet accents
   ============================================ */

[data-theme="luxe"] {
  --bg: #0b1020;
  --bg-elevated: #131a2e;
  --bg-sidebar: #0f1525;
  --bg-card: #151c33;
  --bg-hover: #1c2541;
  --bg-subtle: #1a2139;

  --text-primary: #f1f5fb;
  --text-secondary: #a8b3c7;
  --text-tertiary: #6a7690;
  --text-muted: #4a5570;

  --border: #232c47;
  --border-strong: #303a58;

  --accent: #d4af6f;           /* warm gold */
  --accent-hover: #e6c18a;
  --accent-soft: #2a2419;
  --accent-text: #e6c18a;

  --success: #34d399;
  --success-soft: #0f2a22;
  --warning: #fbbf24;
  --warning-soft: #2a2212;
  --danger: #f87171;
  --danger-soft: #2a1515;
  --info: #818cf8;
  --info-soft: #1a1a33;
  --purple: #a78bfa;
  --purple-soft: #231a33;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow: 0 4px 14px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.2);
  --shadow-lg: 0 20px 50px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(0, 0, 0, 0.3);

  --radius-sm: 6px;
  --radius: 12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 3: Warm Pastel
   Cream + soft pink/mint
   ============================================ */

[data-theme="warm"] {
  --bg: #fbf6f1;
  --bg-elevated: #ffffff;
  --bg-sidebar: #fff8f1;
  --bg-card: #ffffff;
  --bg-hover: #fbf1e8;
  --bg-subtle: #f7ede3;

  --text-primary: #2b1810;
  --text-secondary: #5c4a3e;
  --text-tertiary: #8a7a6e;
  --text-muted: #b8ab9f;

  --border: #ecdfd1;
  --border-strong: #d9c8b5;

  --accent: #c2675b;           /* terracotta */
  --accent-hover: #a7544a;
  --accent-soft: #fbeae6;
  --accent-text: #8e3e34;

  --success: #5b8a72;
  --success-soft: #e3efe8;
  --warning: #c9944a;
  --warning-soft: #faf0d9;
  --danger: #b14848;
  --danger-soft: #f5dede;
  --info: #5b7aa3;
  --info-soft: #e0e9f3;
  --purple: #9579a5;
  --purple-soft: #ece4f1;

  --shadow-sm: 0 1px 2px rgba(43, 24, 16, 0.05);
  --shadow: 0 3px 10px rgba(43, 24, 16, 0.07), 0 1px 2px rgba(43, 24, 16, 0.04);
  --shadow-lg: 0 16px 40px rgba(43, 24, 16, 0.10), 0 3px 8px rgba(43, 24, 16, 0.06);

  --radius-sm: 6px;
  --radius: 12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 4: Sage — زيتوني هادئ
   ============================================ */

[data-theme="sage"] {
  --bg:          #f2f5f0;
  --bg-elevated: #ffffff;
  --bg-sidebar:  #f8faf6;
  --bg-card:     #ffffff;
  --bg-hover:    #eaf0e6;
  --bg-subtle:   #e8ede4;

  --text-primary:   #1a2e1a;
  --text-secondary: #3d5c3a;
  --text-tertiary:  #7a9475;
  --text-muted:     #b2c5ae;

  --border:       #d4e0ce;
  --border-strong:#bdd1b6;

  --accent:       #4a7c59;
  --accent-hover: #3a6347;
  --accent-soft:  #e8f5ec;
  --accent-text:  #2d5c3c;

  --success:      #2e7d52;
  --success-soft: #d1f0e0;
  --warning:      #c9944a;
  --warning-soft: #faf0d9;
  --danger:       #c0392b;
  --danger-soft:  #fde8e6;
  --info:         #3b7dd8;
  --info-soft:    #ddeeff;
  --purple:       #7c5cbf;
  --purple-soft:  #ede8f8;

  --shadow-sm: 0 1px 2px rgba(26,46,26,.04);
  --shadow:    0 2px 8px rgba(26,46,26,.06), 0 1px 2px rgba(26,46,26,.04);
  --shadow-lg: 0 12px 32px rgba(26,46,26,.10), 0 2px 8px rgba(26,46,26,.06);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 5: Ocean — أزرق عميق حديث
   ============================================ */

[data-theme="ocean"] {
  --bg:          #e8f4fd;
  --bg-elevated: #ffffff;
  --bg-sidebar:  #f0f8ff;
  --bg-card:     #ffffff;
  --bg-hover:    #d4ecfb;
  --bg-subtle:   #e0f0fa;

  --text-primary:   #0a2540;
  --text-secondary: #1a4971;
  --text-tertiary:  #5a87b8;
  --text-muted:     #99bedd;

  --border:        #c2ddf5;
  --border-strong: #a8cff0;

  --accent:       #0070cc;
  --accent-hover: #005aad;
  --accent-soft:  #ddeeff;
  --accent-text:  #004a99;

  --success:      #0a8a5f;
  --success-soft: #d0f5e8;
  --warning:      #d4890a;
  --warning-soft: #fff2d0;
  --danger:       #d63031;
  --danger-soft:  #fde8e8;
  --info:         #6c5ce7;
  --info-soft:    #ebe8fd;
  --purple:       #a855f7;
  --purple-soft:  #f3e8ff;

  --shadow-sm: 0 1px 2px rgba(10,37,64,.05);
  --shadow:    0 2px 8px rgba(10,37,64,.07), 0 1px 2px rgba(10,37,64,.04);
  --shadow-lg: 0 12px 32px rgba(10,37,64,.12), 0 2px 8px rgba(10,37,64,.06);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 6: Blush — وردي ناعم (مناسب للأطفال)
   ============================================ */

[data-theme="blush"] {
  --bg:          #fdf4f6;
  --bg-elevated: #ffffff;
  --bg-sidebar:  #fff8fa;
  --bg-card:     #ffffff;
  --bg-hover:    #fce8ed;
  --bg-subtle:   #faeef2;

  --text-primary:   #2d1420;
  --text-secondary: #6b3a4e;
  --text-tertiary:  #b07a90;
  --text-muted:     #d4afc0;

  --border:        #f0d0dc;
  --border-strong: #e8b8ca;

  --accent:       #d4547a;
  --accent-hover: #bc3d63;
  --accent-soft:  #fde8ef;
  --accent-text:  #9c2a4f;

  --success:      #5a9e6f;
  --success-soft: #e0f5e8;
  --warning:      #d4890a;
  --warning-soft: #fff2d0;
  --danger:       #c0392b;
  --danger-soft:  #fde8e6;
  --info:         #5b7dd8;
  --info-soft:    #dde8ff;
  --purple:       #9b59b6;
  --purple-soft:  #f0e6f8;

  --shadow-sm: 0 1px 2px rgba(45,20,32,.04);
  --shadow:    0 2px 8px rgba(45,20,32,.06), 0 1px 2px rgba(45,20,32,.04);
  --shadow-lg: 0 12px 32px rgba(45,20,32,.10), 0 2px 8px rgba(45,20,32,.06);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 7: Midnight — أسود فاخر + أحمر
   ============================================ */

[data-theme="midnight"] {
  --bg:          #080c10;
  --bg-elevated: #0f1520;
  --bg-sidebar:  #0b1018;
  --bg-card:     #121a28;
  --bg-hover:    #1a2438;
  --bg-subtle:   #151e2e;

  --text-primary:   #f0f4fc;
  --text-secondary: #9aabc4;
  --text-tertiary:  #546880;
  --text-muted:     #334455;

  --border:        #1e2c40;
  --border-strong: #273850;

  --accent:       #e84545;
  --accent-hover: #cc2e2e;
  --accent-soft:  #2a1010;
  --accent-text:  #ff7070;

  --success:      #22d196;
  --success-soft: #0a2a20;
  --warning:      #f5b731;
  --warning-soft: #2a2008;
  --danger:       #ff4757;
  --danger-soft:  #2a0f12;
  --info:         #6c9bff;
  --info-soft:    #101830;
  --purple:       #bd93f9;
  --purple-soft:  #1e1030;

  --shadow-sm: 0 1px 3px rgba(0,0,0,.4);
  --shadow:    0 4px 14px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.3);
  --shadow-lg: 0 20px 50px rgba(0,0,0,.6), 0 4px 12px rgba(0,0,0,.4);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

[data-theme="midnight"] .btn.primary { color: #ffffff; }

[data-theme="midnight"] .sidebar-logo-mark { color: #fff; }

[data-theme="midnight"] .nav-item.active {
  background: var(--bg-hover);
  color: var(--accent-text);
}

/* ADR-205: per-theme accent finishes for the 2 new dark themes. Obsidian's
   champagne accent is LIGHT -> dark text on primary buttons (like luxe gold);
   indigo's periwinkle is light-ish -> dark text too. Midnight's red is dark
   -> white text, which the [data-theme-mode="dark"] generic block does NOT
   touch (button text colour is accent-luminance-specific, kept per theme). */

[data-theme="obsidian"] .btn.primary { color: #14110a; }

[data-theme="obsidian"] .sidebar-logo-mark { color: var(--accent); }

[data-theme="obsidian"] .nav-item.active { background: var(--bg-hover); color: var(--accent-text); }

[data-theme="indigo"] .btn.primary { color: #0c0f25; }

[data-theme="indigo"] .sidebar-logo-mark { color: var(--accent); }

[data-theme="indigo"] .nav-item.active { background: var(--bg-hover); color: var(--accent-text); }

/* Petrol's mint accent is LIGHT -> dark text on primary buttons, same
   family as obsidian/indigo above. */

[data-theme="petrol"] .btn.primary { color: #04222a; }

[data-theme="petrol"] .sidebar-logo-mark { color: var(--accent); }

[data-theme="petrol"] .nav-item.active { background: var(--bg-hover); color: var(--accent-text); }

/* ============================================
   THEME 8: Desert — رملي دافئ شرقي
   ============================================ */

[data-theme="desert"] {
  --bg:          #f5efe6;
  --bg-elevated: #fdf8f2;
  --bg-sidebar:  #f8f2ea;
  --bg-card:     #fdf8f2;
  --bg-hover:    #ede4d4;
  --bg-subtle:   #ede4d6;

  --text-primary:   #2c1a08;
  --text-secondary: #6b4c28;
  --text-tertiary:  #a07c58;
  --text-muted:     #c9ab8a;

  --border:        #e0cdb0;
  --border-strong: #ccb898;

  --accent:       #c47c2a;
  --accent-hover: #a86520;
  --accent-soft:  #f8eddc;
  --accent-text:  #8a5010;

  --success:      #5a8a42;
  --success-soft: #e2f0d8;
  --warning:      #d45f0a;
  --warning-soft: #fde8d0;
  --danger:       #c0392b;
  --danger-soft:  #fde0de;
  --info:         #4a7abf;
  --info-soft:    #dce8f5;
  --purple:       #8e5fad;
  --purple-soft:  #ede0f8;

  --shadow-sm: 0 1px 2px rgba(44,26,8,.05);
  --shadow:    0 2px 8px rgba(44,26,8,.07), 0 1px 2px rgba(44,26,8,.04);
  --shadow-lg: 0 12px 32px rgba(44,26,8,.10), 0 2px 8px rgba(44,26,8,.06);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 9: Violet — بنفسجي راقي مميز
   ============================================ */

[data-theme="violet"] {
  --bg:          #f5f0ff;
  --bg-elevated: #ffffff;
  --bg-sidebar:  #f9f5ff;
  --bg-card:     #ffffff;
  --bg-hover:    #ede4ff;
  --bg-subtle:   #ece5ff;

  --text-primary:   #1e0a40;
  --text-secondary: #4a2d80;
  --text-tertiary:  #8b6abf;
  --text-muted:     #bba8e0;

  --border:        #ddd0f8;
  --border-strong: #ccb8f5;

  --accent:       #7c3aed;
  --accent-hover: #6425d0;
  --accent-soft:  #ede9fe;
  --accent-text:  #5b21b6;

  --success:      #059669;
  --success-soft: #d1fae5;
  --warning:      #d97706;
  --warning-soft: #fef3c7;
  --danger:       #dc2626;
  --danger-soft:  #fee2e2;
  --info:         #2563eb;
  --info-soft:    #dbeafe;
  --purple:       #9333ea;
  --purple-soft:  #f3e8ff;

  --shadow-sm: 0 1px 2px rgba(30,10,64,.04);
  --shadow:    0 2px 8px rgba(30,10,64,.07), 0 1px 2px rgba(30,10,64,.04);
  --shadow-lg: 0 12px 32px rgba(30,10,64,.12), 0 2px 8px rgba(30,10,64,.06);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 10: Pearl  (premium LIGHT, ADR-205)
   Warm pearl-white + refined emerald-teal. A calmer,
   warmer alternative to "clean" (which is cool blue-grey).
   ============================================ */

[data-theme="pearl"] {
  --bg:          #f6f4ef;
  --bg-elevated: #fffdf9;
  --bg-sidebar:  #fffdf9;
  --bg-card:     #fffdf9;
  --bg-hover:    #f0ece3;
  --bg-subtle:   #efeae0;

  --text-primary:   #1c2128;
  --text-secondary: #55606e;
  --text-tertiary:  #8a94a2;
  --text-muted:     #b7bfc9;

  --border:        #e7e1d6;
  --border-strong: #d8cfc0;

  --accent:       #0f8a7e;   /* refined emerald-teal */
  --accent-hover: #0c7064;
  --accent-soft:  #e3f3f0;
  --accent-text:  #0a5d54;

  --success:      #0f8a5f;
  --success-soft: #ddf2e7;
  --warning:      #b9821f;
  --warning-soft: #faf0d9;
  --danger:       #c4453a;
  --danger-soft:  #f9e3e0;
  --info:         #2563eb;
  --info-soft:    #e1ebfc;
  --purple:       #7c3aed;
  --purple-soft:  #efe9fe;

  --shadow-sm: 0 1px 2px rgba(28,33,40,.05);
  --shadow:    0 3px 10px rgba(28,33,40,.07), 0 1px 2px rgba(28,33,40,.04);
  --shadow-lg: 0 16px 40px rgba(28,33,40,.10), 0 3px 8px rgba(28,33,40,.06);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 11: Obsidian  (premium DARK, ADR-205)
   True-neutral graphite (no blue cast) + champagne/
   platinum accent. Understated luxury, distinct from
   "luxe" (navy blue + gold).
   ============================================ */

[data-theme="obsidian"] {
  --bg:          #0a0a0c;
  --bg-elevated: #141417;
  --bg-sidebar:  #0e0e11;
  --bg-card:     #16161a;
  --bg-hover:    #1f1f24;
  --bg-subtle:   #1a1a1f;

  --text-primary:   #f4f4f6;
  --text-secondary: #a9adb5;
  --text-tertiary:  #6a6e78;
  --text-muted:     #45484f;

  --border:        #26262c;
  --border-strong: #33333a;

  /* ADR-205: neutral graphite glass (no navy cast) for modals + the
     sticky breadcrumb strip — overrides the default navy --glass-bg-dark. */
  --glass-bg-dark:     rgba(22, 22, 26, 0.9);
  --glass-border-dark: rgba(255, 255, 255, 0.07);

  --accent:       #c8a96a;   /* champagne / platinum */
  --accent-hover: #ddc089;
  --accent-soft:  #221d12;
  --accent-text:  #ddc089;

  --success:      #34d399;
  --success-soft: #0f2a22;
  --warning:      #fbbf24;
  --warning-soft: #2a2212;
  --danger:       #f87171;
  --danger-soft:  #2a1515;
  --info:         #8ab4ff;
  --info-soft:    #131a28;
  --purple:       #c4a7fb;
  --purple-soft:  #1f1830;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --shadow:    0 4px 14px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.3);
  --shadow-lg: 0 20px 50px rgba(0,0,0,.6), 0 4px 12px rgba(0,0,0,.4);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 12: Indigo  (premium DARK, ADR-205)
   Deep indigo-navy + electric periwinkle. Modern SaaS
   premium look.
   ============================================ */

[data-theme="indigo"] {
  --bg:          #0c0f25;
  --bg-elevated: #141936;
  --bg-sidebar:  #0f1330;
  --bg-card:     #171d40;
  --bg-hover:    #1f2750;
  --bg-subtle:   #1a2046;

  --text-primary:   #eef0fb;
  --text-secondary: #a6acd0;
  --text-tertiary:  #6770a0;
  --text-muted:     #454d78;

  --border:        #242c52;
  --border-strong: #313a63;

  /* ADR-205: indigo-tinted glass for modals + the sticky breadcrumb strip. */
  --glass-bg-dark:     rgba(20, 25, 54, 0.9);
  --glass-border-dark: rgba(255, 255, 255, 0.08);

  --accent:       #8b8cf9;   /* electric periwinkle */
  --accent-hover: #a3a4fb;
  --accent-soft:  #1a1c3e;
  --accent-text:  #aeaffb;

  --success:      #34d399;
  --success-soft: #0d2a26;
  --warning:      #f5b731;
  --warning-soft: #2a2210;
  --danger:       #fb7185;
  --danger-soft:  #2c1320;
  --info:         #7aa2ff;
  --info-soft:    #141d3a;
  --purple:       #bda4fb;
  --purple-soft:  #1f1b3e;

  --shadow-sm: 0 1px 3px rgba(0,0,0,.4);
  --shadow:    0 4px 14px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.3);
  --shadow-lg: 0 20px 50px rgba(0,0,0,.6), 0 4px 12px rgba(0,0,0,.4);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* ============================================
   THEME 13: Petrol  (premium DARK, 2026-07-29)
   Deep petrol teal + mint accent. Matches the waiting-display
   `clinic-teal` theme, so the clinic screen and the app read as
   one family. Fills the "dark green/teal" gap in the set (pearl
   was the only teal and it is LIGHT).
   ============================================ */

[data-theme="petrol"] {
  --bg:          #071f26;
  --bg-elevated: #0e2f38;
  --bg-sidebar:  #092630;
  --bg-card:     #10353f;
  --bg-hover:    #17434e;
  --bg-subtle:   #123a45;

  --text-primary:   #eaf7f4;
  --text-secondary: #9dbfbe;
  --text-tertiary:  #648487;
  --text-muted:     #445f63;

  --border:        #1c4650;
  --border-strong: #275964;

  /* Petrol-tinted glass for modals + the sticky breadcrumb strip. */
  --glass-bg-dark:     rgba(14, 47, 56, 0.9);
  --glass-border-dark: rgba(255, 255, 255, 0.08);

  --accent:       #3ddcb8;   /* mint */
  --accent-hover: #63e8c9;
  --accent-soft:  #0d3138;
  --accent-text:  #6fe9cd;

  --success:      #34d399;
  --success-soft: #0c2c26;
  --warning:      #f5b731;
  --warning-soft: #2a2513;
  --danger:       #fb7185;
  --danger-soft:  #2c1a22;
  --info:         #67c7ec;
  --info-soft:    #0d2a33;
  --purple:       #b6a8fb;
  --purple-soft:  #1e1e3a;

  --shadow-sm: 0 1px 3px rgba(0,0,0,.4);
  --shadow:    0 4px 14px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.3);
  --shadow-lg: 0 20px 50px rgba(0,0,0,.6), 0 4px 12px rgba(0,0,0,.4);

  --radius-sm: 6px;
  --radius:    12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
}

/* Theme picker dropdown */

/* Theme dropdown (Chrome round 2 / F4b, 2026-07-08 mockup): the SAME anchored
   card as the language .chrome-menu — a clean vertical list of swatch + name +
   a check on the active theme. Anchored under the palette trigger (it lives in
   the relative .topbar-theme), so it follows the button after topbar changes. */

.theme-picker-drop {
  position: absolute;
  top: calc(100% + 8px);
  /* Open INTO the viewport (see .chrome-menu note) - the palette trigger is on
     the inline-start side of the topbar, so pin the menu's END edge. */
  inset-inline-end: 0;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
  z-index: 300;
  min-width: 212px;
  max-width: calc(100vw - 24px);
  max-height: calc(100vh - 96px);
  overflow-y: auto;
}

.theme-picker-drop .tpd-title {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: 4px 11px 6px;
}

.theme-picker-drop .tpd-grid {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* ADR-205: section label (فاتح / غامق) separating the 6 featured themes */

.theme-picker-drop .tpd-section {
  font-size: var(--text-micro);
  font-weight: 700;
  color: var(--text-tertiary);
  letter-spacing: .04em;
  margin: 8px 11px 2px;
}

.theme-picker-drop .tpd-section:first-of-type { margin-top: 2px; }

/* Rows reuse .chrome-menu-item (row + padding + hover + radius); these add the
   swatch + the active-theme accent. */

.theme-picker-drop .tpd-item-main { display: flex; align-items: center; gap: var(--space-2-5); min-width: 0; }

.theme-picker-drop .tpd-swatch {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid rgba(0, 0, 0, .08);
  flex-shrink: 0;
}

.theme-picker-drop .tpd-label { font-weight: 600; }

.theme-picker-drop .tpd-item.active { color: var(--accent); }

/* U-8: hint pointing dev-only themes to the Tweaks panel */

.theme-picker-drop .tpd-hint {
  margin-top: 8px;
  padding: 10px 11px 4px;
  border-top: 1px solid var(--border);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-align: center;
  line-height: 1.5;
}

/* Chrome round 3 (owner 2026-08-13): the popover took in LANGUAGE, so the
   topbar could give up its globe. The divider is the only new furniture — the
   language rows reuse .tpd-item so the two halves of one preferences surface
   cannot drift apart in look. */

.theme-picker-drop .tpd-divider {
  height: 1px;
  margin: 10px 11px 2px;
  background: var(--border);
}

/* The popover is taller now (2 theme groups + a language list). On a short
   phone in landscape that could run off the screen, so it scrolls itself
   rather than being clipped by the viewport. */

.theme-picker-drop {
  max-height: min(70vh, 520px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ============================================
   Layout
   ============================================ */

#root, .app-root {
  width: 100%;
  height: 100dvh;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text-primary);
  display: flex;
  /* Single scroll model: #root is the element-scroller for PRE-AUTH surfaces
     (login / onboarding / landing render directly in #root). For the AUTHED
     app, .app-shell fills #root exactly (100dvh) so #root has no overflow and
     `.main-content` owns the scroll -- no double scrollbar. Either way scrolling
     lives in an ELEMENT (touch-reliable on mobile), never the page/body. */
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

.main-shell {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}

.page-scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ============================================
   Scrollbars
   ============================================ */

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 10px;
  border: 2px solid var(--bg);
}

::-webkit-scrollbar-thumb:hover {
  background: var(--text-muted);
}

/* ============================================
   Reusable bits
   ============================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 10px 16px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: var(--bg-subtle);
  color: var(--text-primary);
  font-weight: 600;
  font-size: var(--text-base);
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-smooth);
  white-space: nowrap;
  font-family: inherit;
}

.btn:hover { background: var(--bg-hover); }

.btn.primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

[data-theme="luxe"] .btn.primary { color: #0b1020; }

.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn.ghost {
  background: transparent;
  color: var(--text-secondary);
}

.btn.ghost:hover { background: var(--bg-hover); color: var(--text-primary); }

.btn.outline {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-primary);
}

.btn.outline:hover { background: var(--bg-hover); }

.btn.danger { background: var(--danger); border-color: var(--danger); color: #fff; }

.btn.danger:hover { background: color-mix(in oklab, var(--danger) 85%, #000); }

.btn.sm { padding: 6px 12px; font-size: var(--text-sm); }

.btn.icon { padding: var(--space-2); }

/* Semantic outline variants (Wave 7 - buttons center / U-btn). The combined
   selectors are 0,3,0 so they beat the standalone .btn.danger / .btn.outline
   (both 0,2,0) regardless of source order. (The receipt-block .btn.danger
   redefinition + its #b91c1c hover died in Hover Wave 3a; the 0,5,0 hovers
   here remain correct.) This is the ONE place a red/amber
   OUTLINE button is defined; screens use these classes, never inline red
   styles (smoke W7-A ratchet enforces it). Class-selectors are order-agnostic,
   so `btn outline danger` (QuickActionsSheet) matches the same rule. */

.btn.danger.outline {
  background: transparent;
  border-color: var(--danger);
  color: var(--danger);
}

.btn.danger.outline:hover:not(:disabled) {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

.btn.warning.outline {
  background: transparent;
  border-color: var(--warning);
  color: var(--warning);
}

.btn.warning.outline:hover:not(:disabled) {
  background: var(--warning-soft);
  border-color: var(--warning);
  color: var(--warning);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  padding: 4px 10px;
  font-size: var(--text-meta);
  font-weight: 600;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  white-space: nowrap;
}

.chip.success { background: var(--success-soft); color: var(--success); }

.chip.warning { background: var(--warning-soft); color: var(--warning); }

.chip.danger { background: var(--danger-soft); color: var(--danger); }

.chip.info { background: var(--info-soft); color: var(--info); }

.chip.accent { background: var(--accent-soft); color: var(--accent-text); }

.chip.purple { background: var(--purple-soft); color: var(--purple); }

/* Anatomy W0-1: tone maps emit muted/outline (INVENTORY_TONE.all, WA/MK
   fallbacks) but no rule existed - both silently rendered as the base chip. */

.chip.muted { background: var(--bg-subtle); color: var(--text-tertiary); }

.chip.outline { background: transparent; border: 1px solid var(--border-strong); color: var(--text-secondary); }

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.input, .select, .textarea {
  width: 100%;
  padding: 10px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-size: var(--text-base);
  transition: all var(--duration-fast) var(--ease-smooth);
  outline: none;
  font-family: inherit;
}

.input:focus, .select:focus, .textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

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

.label {
  display: block;
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 6px;
}

.divider {
  height: 1px;
  background: var(--border);
  margin: 16px 0;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}

/* ============================================
   Sidebar
   ============================================ */

.sidebar {
  width: 260px;
  background: var(--bg-sidebar);
  border-inline-start: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex-shrink: 0;
  transition: width .28s cubic-bezier(.22, 1, .36, 1);
}

.sidebar-logo {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 16px 16px 14px;
}

.sidebar-brand-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1.15;
  min-width: 0;
}

/* V-Logo (ADR-070): width set inline by the ModirifyLogo component
   (size=lg → 220px). Wrapper only owns positioning + the slight
   nudge that visually centers the wordmark inside the sidebar gutter. */

/* Brand mark stands alone (no tile) — the owner's M logo is a complete,
   colored mark; a box only shrank + muddied it. */

.sidebar-brand-mark { flex-shrink: 0; }

/* Wordmark: harmonized with the site type scale — 19px / weight 700
   (a LOADED Cairo weight; 800 rendered faux-bold) + tight tracking. */

.sidebar-logo-title { font-weight: 700; font-size: 19px; letter-spacing: -0.3px; color: var(--text-primary); line-height: 1.15; }

.sidebar-logo-sub { font-size: 11.5px; font-weight: 500; color: var(--text-tertiary); }

/* ============================================
   Sidebar collapse (2026-07-08) - desktop icon rail.
   Toggled by .sidebar--collapsed (state in AppContext.sidebarCollapsed,
   persisted as senan-sidebar-collapsed). NEVER applied to the mobile
   "more" sheet copy (gated on !excludeIds in sidebar.jsx).
   ============================================ */

/* R2 W1c normalize (owner): the Codex one-off treatment (hard-coded white
   gradients + inset white shadows = broken in dark themes, bespoke hover
   scale + ::before blob, custom focus outline) is retired onto the CENTRAL
   vocabulary: the sidebar's unified hover (state-hover-surface + emphasis
   accent border, same as nav items/demo/logout) + the canonical
   --ring-shadow focus ring. The explicit panel icons (its good idea) stay. */

.sidebar-collapse-btn {
  margin-inline-start: auto;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-elevated);
  color: var(--text-tertiary);
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease, transform .15s ease;
}

.sidebar-collapse-icon { transition: transform .25s cubic-bezier(.16, 1, .3, 1); }

.sidebar-collapse-btn:hover {
  background: var(--state-hover-surface);
  color: var(--accent-text);
  border-color: var(--state-emphasis-border);
}

.sidebar-collapse-btn:hover .sidebar-collapse-icon { transform: translateX(1px); }

.sidebar-collapse-btn.is-collapsed:hover .sidebar-collapse-icon { transform: translateX(-1px); }

.sidebar-collapse-btn:active { transform: scale(.97); }

.sidebar-collapse-btn:focus-visible { outline: none; box-shadow: var(--ring-shadow); }

/* nav label + brand text animate their width to 0 so the rail slides shut smoothly */

.nav-item-label {
  overflow: hidden;
  white-space: nowrap;
  max-width: 170px;
  transition: opacity .18s ease, max-width .28s cubic-bezier(.22, 1, .36, 1);
}

.sidebar.sidebar--collapsed { width: 76px; }

.sidebar--collapsed .sidebar-logo { flex-direction: column; gap: var(--space-2); padding: 14px 8px; }

.sidebar--collapsed .sidebar-brand-text { display: none; }

.sidebar--collapsed .sidebar-collapse-btn { margin-inline-start: 0; }

.sidebar--collapsed .nav-item-label { opacity: 0; max-width: 0 !important; }

.sidebar--collapsed .nav-item { justify-content: center; gap: 0; padding-inline: 0; }

.sidebar--collapsed .nav-item:hover { transform: none; }

.sidebar--collapsed .sidebar-legal { display: none; }

.sidebar--collapsed .sidebar-footer {
  justify-content: center;
  flex-direction: column;
  gap: 6px;
  padding-inline: 8px;
}

.sidebar--collapsed .clinic-switcher--sidebar { flex: 0 0 auto; width: 44px; }

.sidebar--collapsed .clinic-switcher-trigger--sidebar {
  width: 44px;
  height: 44px;
  min-height: 44px;
  padding: 3px;
  justify-content: center;
}

.sidebar--collapsed .clinic-switcher-trigger-copy,
.sidebar--collapsed .clinic-switcher-trigger-caret { display: none; }

/* Demo-mode toggle (2026-07-08) - sits above the sidebar user card. UI stub. */

.sidebar-demo {
  display: flex;
  align-items: center;
  gap: 11px;
  width: calc(100% - 24px);
  margin: 0 12px 8px;
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg-elevated);
  cursor: pointer;
  text-align: start;
  transition: background .15s ease, border-color .15s ease;
}

@media (hover: hover) {
.sidebar-demo:hover { background: var(--bg-hover); border-color: var(--accent); }
.sidebar-demo:disabled,
.sidebar-demo.is-busy { cursor: progress; opacity: .72; }
}

/* ── Training Wave 4.1: persistent identity + optional guided path ─────
   The bar identifies the isolated tenant and suggests one next station. It
   never blocks navigation or creates a second dashboard. */

.training-experience-banner {
  position: sticky;
  top: var(--topbar-height, 64px);
  z-index: 34;
  display: grid;
  grid-template-columns: auto minmax(320px, 1fr) auto;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
  padding: 8px 22px;
  border-bottom: 1px solid color-mix(in srgb, var(--warning) 26%, var(--border));
  background:
    linear-gradient(100deg, color-mix(in srgb, var(--warning) 10%, var(--surface)) 0%,
      color-mix(in srgb, var(--accent) 7%, var(--surface)) 100%);
  box-shadow: 0 8px 24px rgba(15, 23, 42, .05);
  backdrop-filter: blur(16px);
  overflow: hidden;
  max-height: 240px;
  transition:
    transform var(--state-duration) var(--state-ease),
    opacity var(--state-duration) var(--state-ease),
    max-height var(--state-duration) var(--state-ease),
    min-height var(--state-duration) var(--state-ease),
    padding-block var(--state-duration) var(--state-ease),
    border-color var(--state-duration) var(--state-ease);
}

.main.chrome-hidden .training-experience-banner {
  min-height: 0;
  max-height: 0;
  padding-block: 0;
  border-bottom-color: transparent;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-100%);
}

.training-experience-copy,
.training-experience-actions { display: flex; align-items: center; gap: 9px; min-width: 0; }

.training-experience-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 6px 10px;
  border-radius: var(--radius-pill);
  color: color-mix(in srgb, var(--warning) 78%, var(--text-primary));
  background: color-mix(in srgb, var(--warning) 13%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warning) 28%, transparent);
  font-size: 11px;
  font-weight: 800;
}

.training-experience-note {
  overflow: hidden;
  color: var(--text-secondary);
  font-size: 11.5px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.training-experience-actions .btn { min-height: 31px; white-space: nowrap; }

.training-guide {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-width: 0;
  max-width: 760px;
  padding: 5px 7px 5px 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 17%, var(--border));
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
}

.training-guide-progress {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--accent-text);
  font-size: 10.5px;
  font-weight: 800;
  white-space: nowrap;
}

.training-guide-pulse,
.training-guide-nav-beacon {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 34%, transparent);
  animation: training-guide-pulse 2.1s ease-out infinite;
}

.training-guide-copy { display: grid; min-width: 0; gap: 1px; }

.training-guide-copy strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: 11.5px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.training-guide-copy small {
  overflow: hidden;
  color: var(--text-tertiary);
  font-size: 10.5px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.training-guide-next { justify-content: center; min-height: 30px; white-space: nowrap; }

.training-guide-last {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--accent-text);
  font-size: 10.5px;
  font-weight: 700;
  white-space: nowrap;
}

.nav-item.training-guide-target .icon-box { position: relative; }

.training-guide-nav-beacon {
  position: absolute;
  inset-inline-end: -4px;
  top: -3px;
  display: inline-block;
}

@keyframes training-guide-pulse {
  65%, 100% { box-shadow: 0 0 0 9px transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .training-guide-pulse,
  .training-guide-nav-beacon { animation: none; }
  .training-experience-banner { transition: none; }
}

.training-setup-modal .rm-body { padding-block-start: 18px; }

.training-setup-options { display: grid; gap: 9px; text-align: start; }

.training-setup-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 68px;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 16px;
  color: var(--text-primary);
  background: var(--surface-raised, var(--surface));
  cursor: pointer;
  transition: border-color .16s ease, background .16s ease, box-shadow .16s ease;
}

.training-setup-option:hover {
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
  background: color-mix(in srgb, var(--accent) 5%, var(--surface));
  box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 10%, transparent);
}

.training-setup-option:disabled { cursor: progress; opacity: .65; }

.training-setup-option-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: 13px;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
}

.training-setup-option-copy { display: grid; flex: 1 1 auto; gap: 3px; }

.training-setup-option-copy strong { font-size: 13.5px; }

.training-setup-option-copy small { color: var(--text-tertiary); font-size: 11.5px; line-height: 1.5; }

@media (max-width: 1180px) {
  .training-experience-banner { grid-template-columns: minmax(0, 1fr) auto; }
  .training-guide { grid-column: 1 / -1; grid-row: 2; max-width: none; }
}

@media (max-width: 768px) {
  .training-experience-banner { position: relative; top: auto; grid-template-columns: 1fr; padding: 9px 12px; }
  .training-experience-note { white-space: normal; }
  .training-guide { grid-column: auto; grid-row: auto; }
  .training-experience-actions { overflow-x: auto; padding-bottom: 1px; }
  .training-experience-actions .btn { flex: 0 0 auto; }
}

@media (max-width: 520px) {
  .training-guide { grid-template-columns: auto minmax(0, 1fr); }
  .training-guide-next,
  .training-guide-last { grid-column: 1 / -1; width: 100%; }
}

.sidebar-demo-icon {
  width: 34px; height: 34px; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--accent-soft); color: var(--accent-text);
}

.sidebar-demo-text { flex: 1; min-width: 0; line-height: 1.25; text-align: start; }

.sidebar-demo-title { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); white-space: nowrap; }

.sidebar-demo-sub { font-size: 10.5px; color: var(--text-tertiary); white-space: nowrap; }

.sidebar-demo-switch {
  flex-shrink: 0;
  width: 38px; height: 22px;
  border-radius: var(--radius-pill);
  background: var(--border);
  position: relative;
  transition: background .2s ease;
}

.sidebar-demo.is-on .sidebar-demo-switch { background: var(--accent); }

.sidebar-demo-knob {
  position: absolute;
  top: 2px; inset-inline-start: 2px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
  transition: inset-inline-start .2s cubic-bezier(.22, 1, .36, 1);
}

.sidebar-demo.is-on .sidebar-demo-knob { inset-inline-start: 18px; }

/* collapsed rail: icon only */

.sidebar--collapsed .sidebar-demo { justify-content: center; width: 52px; margin-inline: auto; padding: var(--space-2); }

.sidebar--collapsed .sidebar-demo-text,
.sidebar--collapsed .sidebar-demo-switch { display: none; }

/* Official quick channels — one central SocialLinks primitive fed by the
   SaaS support registry. A compact glass dock gives WhatsApp + social links
   a clear home without turning the operational rail into another help page. */

.social-links--sidebar {
  width: calc(100% - 24px);
  min-height: 40px;
  margin: 0 12px 7px;
  padding: 5px 6px;
  border: 1px solid color-mix(in srgb, var(--accent) 12%, var(--border));
  border-radius: 15px;
  background:
    radial-gradient(circle at 18% 0%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 52%),
    linear-gradient(145deg,
      color-mix(in srgb, #fff 54%, var(--bg-card)),
      color-mix(in srgb, var(--accent) 3%, var(--bg-elevated)));
  box-shadow:
    0 8px 22px rgba(15, 23, 42, .055),
    inset 0 1px 0 color-mix(in srgb, #fff 76%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
}

.social-link {
  --social-brand: var(--accent);
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  display: grid;
  place-items: center;
  border-radius: 9px;
  border: 0;
  color: color-mix(in srgb, var(--social-brand) 72%, var(--text-secondary));
  background: color-mix(in srgb, var(--social-brand) 9%, rgba(255, 255, 255, .72));
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--social-brand) 13%, transparent),
    inset 0 1px 0 color-mix(in srgb, #fff 78%, transparent);
  text-decoration: none;
  isolation: isolate;
  position: relative;
  overflow: hidden;
  animation: socialLinkIn .28s var(--ease-out, ease) both;
  animation-delay: calc(var(--social-index, 0) * 35ms);
  transition:
    transform 160ms var(--ease-out, ease),
    color 160ms var(--ease-out, ease),
    border-color 160ms var(--ease-out, ease),
    background 160ms var(--ease-out, ease),
    box-shadow 160ms var(--ease-out, ease);
}

.social-link::before {
  content: '';
  position: absolute;
  inset: 1px;
  z-index: -1;
  border-radius: var(--radius-md);
  background: radial-gradient(circle at 28% 8%, color-mix(in srgb, #fff 50%, transparent), transparent 55%);
  opacity: .72;
}

.social-links__divider {
  width: 1px;
  height: 18px;
  flex: 0 0 1px;
  margin-inline: 2px;
  border-radius: var(--radius-pill);
  background: linear-gradient(transparent, color-mix(in srgb, var(--text-muted) 32%, transparent), transparent);
}

@media (hover: hover) {
  .social-link:hover {
    transform: translateY(var(--lift-2));
    color: #fff;
    background: var(--social-brand);
    box-shadow:
      0 7px 16px color-mix(in srgb, var(--social-brand) 27%, transparent),
      inset 0 1px 0 color-mix(in srgb, #fff 34%, transparent);
  }
}

.social-link:active { transform: translateY(0) scale(.96); }

.social-link:focus-visible {
  outline: none;
  box-shadow: var(--ring-shadow), 0 7px 16px color-mix(in srgb, var(--social-brand) 22%, transparent);
}

.sidebar--collapsed .social-links--sidebar { display: none; }

/* Short desktop viewports: preserve the whole persistent rail (including the
   account/logout footer) instead of clipping its newest low-priority section.
   Density changes are vertical only; widths, hit targets, and hierarchy stay. */

@media (min-width: 769px) and (max-height: 760px) {
  .sidebar-logo { padding-block: 5px 4px; }
  .sidebar .nav-items { gap: 0; }
  .sidebar .nav-item { padding-block: 7px; }
  .sidebar .sidebar-nav-divider { margin-block: 1px; }
  .sidebar-demo { margin-bottom: 4px; padding-block: 4px; }
  .sidebar-demo-sub { display: none; }
  .social-links--sidebar { min-height: 34px; margin-bottom: 4px; padding-block: 2px; }
  .sidebar .sidebar-footer { padding-block: 3px; margin-bottom: 2px; }
  .sidebar-footer .avatar { width: 36px; height: 36px; }
}

/* Extreme short-height fallback. This trims two pixels from each nav row but
   keeps the normal visual rhythm; it is deliberately below common laptop
   viewport heights so the regular rail never looks stacked. */

@media (min-width: 769px) and (max-height: 660px) {
  .sidebar .nav-item { padding-block: 5px; }
  .sidebar-logo { padding-block: 3px 2px; }
}

@keyframes socialLinkIn {
  from { opacity: 0; transform: translateY(5px) scale(.94); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar, .sidebar-collapse-btn, .nav-item-label,
  .sidebar-demo-switch, .sidebar-demo-knob, .social-link { transition: none; animation: none; }
}

/* Central 2×2 decision board ------------------------------------------------
   One visual layer only: self-explanatory quadrants → expandable evidence.
   Domain screens provide vocabulary and RankList rows; spacing, motion and
   responsive behavior live here. */

.decision-matrix { min-width: 0; }

.decision-matrix__quadrant {
  --matrix-tone: var(--accent); --matrix-soft: var(--accent-soft);
}

.decision-matrix__quadrant.is-success { --matrix-tone: var(--success); --matrix-soft: var(--success-soft); }

.decision-matrix__quadrant.is-warning { --matrix-tone: var(--warning); --matrix-soft: var(--warning-soft); }

.decision-matrix__quadrant.is-danger { --matrix-tone: var(--danger); --matrix-soft: var(--danger-soft); }

.decision-matrix__quadrant.is-info { --matrix-tone: var(--info); --matrix-soft: var(--info-soft); }

.decision-matrix__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2-5); }

.decision-matrix__quadrant {
  min-width: 0; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--card-bg); box-shadow: var(--shadow-xs);
  animation: decision-matrix-in .42s cubic-bezier(.22,.8,.28,1) both;
  animation-delay: calc(90ms + var(--matrix-i) * 55ms);
}

.decision-matrix__quadrant::before { content: ''; display: block; height: 3px; background: var(--matrix-tone); }

.decision-matrix__quadrant-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2-5);
  padding: 12px 14px; background: linear-gradient(180deg, color-mix(in srgb, var(--matrix-soft) 62%, transparent), transparent);
}

.decision-matrix__quadrant-head h3 { display: flex; align-items: center; gap: 7px; margin: 0; color: var(--text-primary); font-size: var(--text-sm); }

.decision-matrix__quadrant-head h3 span { color: var(--matrix-tone); }

.decision-matrix__count {
  flex: none; padding: 3px 7px; border-radius: var(--radius-pill);
  background: var(--matrix-soft); color: var(--matrix-tone); font-size: 9px; font-weight: 900;
}

.decision-matrix__empty { padding: 22px 14px; color: var(--text-tertiary); text-align: center; font-size: var(--text-xs); }

.decision-matrix__empty.is-page { padding: 34px 14px; }

.decision-matrix__detail { padding: 10px 12px; }

.decision-matrix__detail-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; }

.decision-matrix__detail-metrics > div { min-width: 0; padding: var(--space-2); border-radius: var(--radius-md); background: var(--card-bg); text-align: center; }

.decision-matrix__detail-metrics span,
.decision-matrix__detail-metrics strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.decision-matrix__detail-metrics span { color: var(--text-tertiary); font-size: 9px; }

.decision-matrix__detail-metrics strong { margin-top: 3px; color: var(--text-primary); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }

@keyframes decision-matrix-in {
  from { opacity: 0; transform: translateY(6px) scale(.99); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (hover: hover) {
  .decision-matrix__quadrant { transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease; }
  .decision-matrix__quadrant:hover { border-color: color-mix(in srgb, var(--matrix-tone) 28%, var(--border)); box-shadow: var(--shadow-sm); transform: translateY(var(--lift-1)); }
}

@media (max-width: 760px) {
  .decision-matrix__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 540px) {
  .decision-matrix__detail-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  .decision-matrix__quadrant { animation: none !important; }
}

/* V-Logo (ADR-070) — Unified ModirifyLogo base styles.
   Component lives in app/src/ui/logo.jsx and sets width inline via
   the LOGO_SIZES scale (xs=80, sm=140, md=200, lg=220, xl=280).
   These base styles give every instance the same display/object
   behavior + a smooth width transition so the brand "breathes"
   between contexts (login → onboarding → sidebar). */

.modirify-logo {
  display: block;
  height: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: center;
  transition: var(--logo-transition);
}

@media (prefers-reduced-motion: reduce) {
  .modirify-logo { transition: none; }
}

.nav-items {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0 10px;
}

/* Anatomy W-SIDEBAR (owner 2026-07-17): ONE adaptive rail recipe, driven by
   the same nav model that applies permissions and plan visibility.
   - FILL mode (.sidebar-navigation--fill, >= FILL_MODE_MIN_ITEMS visible
     screens): the full-permission look — groups distribute through the WHOLE
     column height (per-group flex-grow via --nav-count + space-evenly).
   - Base (limited-permission rails): the navigation stacks compact at the
     top, and the ONE intentional breathing zone lives between it and the
     pinned .sidebar-bottom cluster — no more 3 screens stretched across the
     whole column. Both modes scroll internally on short windows, so the
     account card never disappears. The mode flips automatically as
     permissions / plan / admin visibility change the visible count. */

.sidebar-navigation {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 2px;
  padding-block: 4px;
  overflow-y: auto;
  scrollbar-width: thin;
}

.sidebar-navigation-group {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
}

.sidebar-navigation-group > .nav-items {
  flex: 0 0 auto;
  justify-content: flex-start;
}

.sidebar-navigation--fill .sidebar-navigation-group {
  flex: var(--nav-count, 1) 1 0;
  min-height: 0;
  /* Density governor (replaces the old tall-tablet media exception at the
     ROOT): fill mode may stretch up to ~64px per screen and no further. On a
     landscape desktop that is full-height distribution; on very tall columns
     (portrait tablets, 4K) the rail stops at a comfortable rhythm and the
     leftover settles above the pinned bottom cluster - device-agnostic. */
  max-height: calc(var(--nav-count, 1) * 64px);
}

.sidebar-navigation--fill .sidebar-navigation-group > .nav-items {
  flex: 1 1 auto;
  min-height: 0;
  justify-content: space-evenly;
}

/* Anatomy W-SIDEBAR: the whole under-nav cluster (theme / demo / social /
   account card) pins to the bottom edge as one unit. In fill mode the nav
   consumes the free space so the pin is a no-op — same DOM, both modes. */

.sidebar-bottom {
  margin-block-start: auto;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
}

.sidebar-nav-divider {
  height: 1px;
  flex: 0 0 1px;
  margin: 6px 14px;
  background: var(--border);
  opacity: .6;
}

.sidebar-navigation--fill .sidebar-nav-divider { margin: 2px 14px; }

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px 12px;
  border-radius: var(--radius);
  color: var(--text-secondary);
  font-weight: 500;
  font-size: var(--text-base);
  cursor: pointer;
  border: 1px solid transparent;
  background: transparent;
  text-align: start;
  width: 100%;
  transition: all var(--duration-fast) var(--ease-smooth);
  font-family: inherit;
}

/* Unified sidebar row hover (2026-07-08): same accent-border highlight as the
   .sidebar-demo toggle - one consistent mouseover treatment across the rail.
   border-box (global) means the 1px border adds no layout shift at rest. */

@media (hover: hover) {
.nav-item:hover { background: var(--bg-hover); color: var(--text-primary); border-color: var(--accent); }
}

/* (footer logout hover lives in the premium layer - the single
   ".sidebar-footer .icon-btn:hover" rule there; kept out of here to avoid
   a duplicate that the later premium rule would override anyway.) */

.nav-item.active {
  background: var(--accent-soft);
  color: var(--accent-text);
  font-weight: 700;
}

[data-theme="luxe"] .nav-item.active { background: var(--bg-hover); color: var(--accent); }

.nav-item .icon-box {
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
}

/* ADR-145 F3: legal links cluster sitting between the mini-stats card
   and the avatar row. Small (11px), muted, single line on desktop;
   wraps on narrow rails without breaking the layout. The dots are
   visual separators marked aria-hidden so screen readers skip them. */

.sidebar-legal {
  padding: 6px 14px 8px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 6px;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  border-top: 1px solid var(--border);
  opacity: 0.85;
}

.sidebar-legal a {
  color: var(--text-tertiary);
  text-decoration: none;
  padding: 2px 4px;
  border-radius: var(--radius-xs);
  transition: color 160ms ease, background 160ms ease;
}

.sidebar-legal a:hover,
.sidebar-legal a:focus-visible {
  color: var(--accent);
  background: var(--bg-subtle);
  outline: none;
}

.sidebar-legal span[aria-hidden] {
  opacity: 0.4;
  user-select: none;
}

.sidebar-footer {
  position: relative;
  padding: 9px 12px 11px;
  margin-bottom: 0;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Premium clinic/account switcher. It replaces the tiny topbar dropdown with
   one clear identity surface in the rail, while logout remains a separate
   confirmed action. The list opens upward so it never competes with content. */

.clinic-switcher {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.clinic-switcher-scrim {
  position: fixed;
  inset: 0;
  z-index: 298;
  border: 0;
  background: transparent;
  cursor: default;
}

.clinic-switcher-trigger {
  width: 100%;
  min-width: 0;
  min-height: 48px;
  padding: 5px 7px;
  display: flex;
  align-items: center;
  gap: 9px;
  border: 1px solid transparent;
  border-radius: 14px;
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

.clinic-switcher-trigger:hover:not(:disabled),
.clinic-switcher.is-open .clinic-switcher-trigger {
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 24%, var(--border));
}

.clinic-switcher-trigger:focus-visible {
  outline: none;
  box-shadow: var(--ring-shadow);
}

.clinic-switcher-trigger:active:not(:disabled) { transform: scale(.985); }

.clinic-switcher-trigger:disabled { cursor: default; }

.clinic-switcher-avatar {
  width: 40px;
  height: 40px;
  border-radius: 13px;
  font-size: 13px;
}

.clinic-switcher-trigger-copy,
.clinic-switcher-option-copy,
.clinic-switcher-panel-heading {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.clinic-switcher-trigger-copy { flex: 1 1 auto; gap: 2px; }

.clinic-switcher-trigger-name,
.clinic-switcher-trigger-meta,
.clinic-switcher-option-name,
.clinic-switcher-option-kind {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.clinic-switcher-trigger-name { font-size: 13px; font-weight: 750; line-height: 1.25; }

.clinic-switcher-trigger-meta { font-size: 10.5px; color: var(--text-tertiary); direction: ltr; text-align: start; }

.clinic-switcher-trigger-caret {
  flex: 0 0 auto;
  color: var(--text-tertiary);
  transition: transform .2s var(--ease-smooth), color .15s ease;
}

.clinic-switcher.is-open .clinic-switcher-trigger-caret {
  color: var(--accent);
  transform: rotate(180deg);
}

.clinic-switcher-panel {
  position: absolute;
  inset-inline-start: 0;
  bottom: calc(100% + 9px);
  z-index: 310;
  width: min(236px, calc(100vw - 28px));
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--accent) 16%, var(--border));
  border-radius: 18px;
  background:
    radial-gradient(circle at 12% 0%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 42%),
    color-mix(in srgb, var(--bg-card) 94%, transparent);
  box-shadow:
    0 22px 54px rgba(15, 23, 42, .18),
    0 4px 14px rgba(15, 23, 42, .08),
    inset 0 1px 0 color-mix(in srgb, #fff 72%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  transform-origin: bottom center;
  animation: clinicSwitcherIn .18s var(--ease-spring) both;
}

.clinic-switcher-panel-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 13px 11px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
}

.clinic-switcher-panel-icon {
  width: 36px;
  height: 36px;
  flex: 0 0 36px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  background: var(--accent-soft);
  color: var(--accent-text);
}

.clinic-switcher-panel-heading { gap: 2px; }

.clinic-switcher-panel-heading strong { font-size: 13px; color: var(--text-primary); }

.clinic-switcher-panel-heading small { font-size: 10.5px; color: var(--text-tertiary); line-height: 1.4; }

.clinic-switcher-options {
  max-height: min(308px, calc(100vh - 210px));
  overflow-y: auto;
  padding: 7px;
}

.clinic-switcher-option {
  width: 100%;
  min-width: 0;
  min-height: 54px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  border: 1px solid transparent;
  border-radius: 13px;
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}

.clinic-switcher-option:hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: color-mix(in srgb, var(--accent) 18%, transparent);
}

.clinic-switcher-option.is-current {
  background: color-mix(in srgb, var(--accent) 9%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 20%, var(--border));
}

.clinic-switcher-option:focus-visible { outline: none; box-shadow: var(--ring-shadow); }

.clinic-switcher-option:disabled { cursor: progress; }

.clinic-switcher-option-avatar { border-radius: 11px; }

.clinic-switcher-option-copy { flex: 1 1 auto; gap: 2px; }

.clinic-switcher-option-name { font-size: 12.5px; font-weight: 700; }

.clinic-switcher-option-kind { font-size: 10.5px; color: var(--text-tertiary); }

.clinic-switcher-option-state {
  width: 20px;
  flex: 0 0 20px;
  display: grid;
  place-items: center;
  color: var(--accent);
}

.clinic-switcher-spinner { animation: spin .8s linear infinite; }

.sidebar-logout-btn {
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  border-radius: 12px;
  color: var(--text-tertiary);
}

@keyframes clinicSwitcherIn {
  from { opacity: 0; transform: translateY(8px) scale(.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* Legal links relocated from the sidebar to Settings (2026-07-08) - a centered
   muted footer under the settings shell, reachable on every viewport. */

/* Global app footer (2026-07-08, owner): slim legal + contact bar rendered
   by Router.jsx as the last child of .main-content, so it sits at the natural
   end of EVERY authed screen (not Settings-only). A quiet top border separates
   it from screen content; links tint to accent on hover (interaction-states
   recipe: hover-link). Copy wraps to its own centered line under the links. */

.app-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 18px;
  margin-top: 32px;
  padding: 18px 16px 24px;
  border-top: 1px solid var(--border);
  font-size: var(--text-meta);
  color: var(--text-tertiary);
}

.app-footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 18px;
}

.app-footer-links a {
  color: var(--text-tertiary);
  text-decoration: none;
  transition: color 140ms var(--ease-out, ease);
}

.app-footer-links a:hover { color: var(--accent); text-decoration: underline; }

.app-footer-contact { font-weight: 600; color: var(--text-secondary) !important; }

.app-footer-copy {
  flex-basis: 100%;
  text-align: center;
  color: var(--text-muted);
  margin-top: 2px;
}

.avatar {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--accent), var(--info));
  color: #fff;
  display: grid;
  place-items: center;
  font-weight: 700;
  flex-shrink: 0;
  font-size: var(--text-base);
}

.avatar.sm { width: 30px; height: 30px; font-size: var(--text-meta); border-radius: var(--radius-md); }

.avatar.xs { width: 24px; height: 24px; font-size: var(--text-micro); border-radius: var(--radius-sm); }

.avatar.lg { width: 54px; height: 54px; font-size: 18px; border-radius: 14px; }

/* ============================================
   Topbar
   ============================================ */

.topbar {
  height: 64px;
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: var(--space-4);
  flex-shrink: 0;
  /* Chrome layering lives in the PREMIUM .topbar block (~L5940) - the
     single owner of position/z-index/backdrop-filter. Do not re-declare
     them here: a second declaration is how the dropdown-under-tabs bug
     regressed (owner 2026-07-10). */
  /* Hide-on-scroll (Wave C): animate the slide + space reclaim. */
  transition: transform 0.22s ease, margin-top 0.22s ease;
}

/* Chrome Wave C (2026-07-08, owner): premium screen title = an accent-tinted
   square holding the shared screen glyph (SCREEN_ICONS) + the title text. */

.topbar-title-row { display: flex; align-items: center; gap: var(--space-2-5); min-width: 0; }

/* W-ICON: the topbar compass wears the SAME glass as the header identity
   chip, one size down — the flat --accent-soft square is retired. */

.topbar-title-icon {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  color: var(--accent);
  background:
    linear-gradient(145deg,
      color-mix(in srgb, var(--accent) 22%, var(--bg-elevated)),
      color-mix(in srgb, var(--accent) 6%, var(--bg-elevated)));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
  box-shadow:
    0 2px 8px color-mix(in srgb, var(--accent) 18%, transparent),
    inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent);
}

.topbar-title-main {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  font-weight: 700;
  font-size: 18px;
  color: var(--text-primary);
}

.topbar-title-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

.topbar-title-spinner {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* The language pill RETIRED (Chrome round 3, owner 2026-08-13): language moved
   inside the appearance popover, so the bar has one preferences control
   instead of two. The shared .chrome-menu card below stays - it is the
   general topbar-dropdown look, not the language one. */

/* Shared anchored dropdown card + rows (mockup look) — the ONE source for a
   topbar menu (language now; any future topbar dropdown reuses it). Built on
   the Wave E interaction-states tokens so the hover matches everywhere. */

.chrome-menu {
  position: absolute;
  top: calc(100% + 8px);
  /* Anchor the menu's END edge to the trigger so it opens INTO the viewport.
     The topbar action cluster sits at the inline-start side, so a menu pinned
     at inset-inline-start would spill off-screen (F4b clip bug). */
  inset-inline-end: 0;
  z-index: 300;
  min-width: 168px;
  max-width: calc(100vw - 24px);
  max-height: calc(100vh - 96px);
  overflow-y: auto;
  padding: var(--space-2);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
}

.chrome-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
  width: 100%;
  padding: 9px 11px;
  border: 0;
  background: transparent;
  border-radius: 9px;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  text-align: start;
  transition: background var(--state-duration) var(--state-ease);
}

.chrome-menu-item:hover { background: var(--state-hover-surface); }

.chrome-menu-item.active { color: var(--accent); }

.chrome-menu-check { color: var(--accent); flex-shrink: 0; }

/* Hide-on-scroll (owner: "sticky with scroll, then disappears"): the
   direction-aware scroll signal (lib/screen-tab-scroll.js) toggles
   .chrome-hidden on .main while scrolling DOWN; the topbar slides up and
   reclaims its 64px so the content gets the room. Scroll UP (or near the top /
   on navigation) shows it again. */

.main.chrome-hidden .topbar { transform: translateY(-100%); margin-top: -64px; }

@media (prefers-reduced-motion: reduce) {
  .topbar { transition: none; }
}

.topbar .search {
  flex: 1;
  max-width: 440px;
  position: relative;
}

/* Mobile/tablet search affordance: a magnifier button that expands the search.
   Hidden on desktop (the inline box is always visible there); the <=768px media
   block flips it to inline-flex. */

.topbar-search-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
}

.topbar-search-toggle:hover { background: var(--bg-hover); color: var(--accent); }

/* ADR-287: the topbar center is a LAUNCHER, not an input. It renders as a
   search box so nothing changes visually, but clicking it (or Cmd+K / Ctrl+K
   anywhere) hands the query to the command palette. The old input rules, the
   mobile expand-in-place overlay, and its close (X) are gone with it - the
   phone opens the same palette full-screen instead. */

.topbar .search-launcher {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 40px;
  padding: 9px 14px;
  background: var(--bg-subtle);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text-muted);
  text-align: start;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-fast) var(--ease-smooth);
}

.topbar .search-launcher:hover {
  background: var(--bg-elevated);
  border-color: var(--border);
}

.topbar .search-launcher:focus-visible {
  outline: none;
  background: var(--bg-elevated);
  border-color: var(--border-strong);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

.search-launcher-icon {
  display: inline-flex;
  align-items: center;
  color: var(--text-tertiary);
  flex-shrink: 0;
}

.search-launcher-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-launcher-kbd {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  font-size: var(--text-xs);
  font-family: 'JetBrains Mono', monospace;
  color: var(--text-tertiary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.topbar .search-launcher:hover .search-launcher-kbd,
.topbar .search-launcher:focus-visible .search-launcher-kbd {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
}

.topbar-actions { display: flex; align-items: center; gap: var(--space-2); margin-inline-start: auto; }

/* ══════════════════════════════════════════════════════════════════════════
   Chrome round 3 (owner 2026-08-13) — the topbar is rented, not owned.
   ══════════════════════════════════════════════════════════════════════════
   Measured on the owner's 412px phone: five chrome controls held ~69% of the
   bar and the screen's own name held ~17%, ellipsized to "الاستـ…" on every
   screen. `.topbar-actions` is `flex-shrink: 0`, so the title is served last
   out of whatever the icons leave — the same disease as the screen header's
   uncapped action column, and with touch targets there is no shrinking to do.
   Fewer controls is the only real fix, so the test each one had to pass was:
   does its value CHANGE, and is it needed on EVERY screen? A preference set
   once in the clinic's lifetime fails it, and so does a destination.
     • the language globe  -> folded into the appearance popover (set-once)
     • the announcements bell -> Modir's «التحديثات»
   Owner ruling 2026-08-13, after seeing it: the CLINIC ROOM STAYS in the bar.
   Both were proposed for the move; only the feed actually belonged there. The
   room is a live conversation the clinic is having right now, and its unread
   is something you act on within minutes — one tap, on the bar, is the right
   cost for that. Platform updates are read once a month; one tap deeper is
   free. The bar settles at four, not three.
   The bell below stays MOUNTED — its panel and login splash are portals, and
   it publishes the unread count Modir reads. Only the button is hidden;
   deleting the component would take the feed offline instead of moving it. */

.topbar .ann-icon-btn { display: none; }

.coin-wallet-pill {
  min-width: 66px;
  height: 38px;
  padding: 0 10px 0 8px;
  border-radius: var(--radius-pill);
  border: 1px solid color-mix(in srgb, #d59b19 48%, var(--border));
  background: linear-gradient(135deg, color-mix(in srgb, #fff7d6 88%, var(--bg-elevated)), var(--bg-elevated));
  color: #805900;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font: inherit;
  font-size: 13px;
  font-weight: 900;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(182, 125, 0, .12);
  transition: transform var(--duration-fast) var(--ease-smooth), box-shadow var(--duration-fast) var(--ease-smooth);
}

.coin-wallet-pill:hover { box-shadow: 0 7px 20px rgba(182, 125, 0, .2); }

.coin-wallet-pill:focus-visible { outline: 3px solid color-mix(in srgb, #d59b19 28%, transparent); outline-offset: 2px; }

.coin-wallet-mark {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  color: #6c4700;
  background: linear-gradient(145deg, #fff4a8 0%, #f3c94e 48%, #c98b12 100%);
  border: 1px solid rgba(121, 76, 0, .28);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.72), 0 2px 6px rgba(144, 93, 0, .2);
  font-family: Georgia, serif;
  font-size: 13px;
  font-weight: 900;
}

.coin-wallet-mark--hero { width: 52px; height: 52px; font-size: 26px; box-shadow: inset 0 2px 0 rgba(255,255,255,.72), 0 8px 22px rgba(144,93,0,.2); }

.coin-wallet-panel { display: grid; gap: 14px; }

.coin-wallet-hero {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 18px;
  border-radius: 18px;
  background: linear-gradient(135deg, #fff9df, color-mix(in srgb, #f3c94e 12%, var(--bg-elevated)));
  border: 1px solid color-mix(in srgb, #d59b19 34%, var(--border));
}

.coin-wallet-hero-label { color: var(--text-secondary); font-size: 12px; font-weight: 800; }

.coin-wallet-hero-value { color: var(--text-primary); font-size: 32px; line-height: 1.15; font-weight: 950; }

.coin-wallet-hero-symbol { color: #805900; font-size: 12px; font-weight: 900; }

.coin-wallet-hold-note,.coin-wallet-expiry-note { padding: 10px 12px; border-radius: 12px; background: var(--bg-subtle); color: var(--text-secondary); font-size: 12px; }

.coin-wallet-hold-note { display: flex; align-items: center; gap: 7px; }

.coin-wallet-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

.coin-wallet-movements { display: grid; gap: 8px; }

.coin-wallet-movement { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; padding: 11px 12px; border: 1px solid var(--border); border-radius: 13px; background: var(--bg-elevated); }

.coin-wallet-movement-icon { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; }

.coin-wallet-movement-icon.is-credit { color: var(--success); background: color-mix(in srgb, var(--success) 10%, transparent); }

.coin-wallet-movement-icon.is-debit { color: #a56d00; background: rgba(213,155,25,.12); }

.coin-wallet-movement-main { min-width: 0; display: grid; gap: 2px; }

.coin-wallet-movement-main strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }

.coin-wallet-movement-main span { color: var(--text-tertiary); font-size: 11px; }

.coin-wallet-credit { color: var(--success); }

.coin-wallet-debit { color: #a56d00; }

.coin-wallet-empty { padding: 24px; text-align: center; border: 1px dashed var(--border); border-radius: 14px; color: var(--text-tertiary); }

.icon-btn {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid transparent;
  cursor: pointer;
  position: relative;
  transition: all var(--duration-fast) var(--ease-smooth);
}

/* Hover Wave 2a: the .icon-btn:hover that lived here (bg-subtle) was dead
   by cascade - the Wave-E token rule later in the file overrode its
   background and repeated its color. ONE source now (the token rule). */

.icon-btn .pulse-dot {
  position: absolute;
  top: 8px;
  inset-inline-end: 9px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  border: 2px solid var(--bg-elevated);
}

/* ── R2 W1d (owner reference shot 2026-07-10): premium topbar icon cluster ──
   The tool icons read as bare PREMIUM GLYPHS (no boxes): circular ghost hit
   areas + the central hover surface. ONE exception by design: the REFRESH is
   a real contained button (border + elevated surface + soft shadow) so the
   cluster has a single anchored control, like the reference. The language
   pill drops its border/box and joins the ghost family. Scoped to .topbar so
   .icon-btn elsewhere (drawers, tables) is untouched. */

.topbar .icon-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  background: transparent;
  border-color: transparent;
  color: var(--text-secondary);
}

/* ONE hover across the whole cluster (owner): the SIDEBAR's central recipe —
   hover surface + accent emphasis border — instead of per-control hovers.
   transform: none kills the premium layer's scale(1.06) here so the feel is
   IDENTICAL to the sidebar (which never zooms). */

.topbar .icon-btn:hover,
.topbar .icon-btn.topbar-refresh:hover {
  background: var(--state-hover-surface);
  border-color: var(--state-emphasis-border);
  color: var(--text-primary);
  transform: none;
}

/* The ONE contained control: refresh. Hovering pre-spins the arrows a notch —
   a quiet "this will act" affordance; the real spin plays while refreshing. */

.topbar .icon-btn.topbar-refresh {
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--text-primary) 6%, transparent);
}

.topbar .icon-btn.topbar-refresh svg { transition: transform .35s cubic-bezier(.16, 1, .3, 1); }

.topbar .icon-btn.topbar-refresh:hover svg { transform: rotate(80deg); }

.topbar .icon-btn:focus-visible { outline: none; box-shadow: var(--ring-shadow); }

@media (prefers-reduced-motion: reduce) {
  .topbar .icon-btn.topbar-refresh svg { transition: none; }
  .topbar .icon-btn.topbar-refresh:hover svg { transform: none; }
}

/* ============================================
   Page header
   ============================================ */

.page-header {
  padding: 24px 32px 8px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-5);
  flex-wrap: wrap;
}

.page-title {
  font-size: 24px;
  font-weight: 800;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

.page-subtitle {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  margin-top: 4px;
}

/* ══════════════════════════════════════════════════════════════════
   W-HDR (owner 2026-07-17) — --screen-inset: THE screen's horizontal
   inset. ONE number, three device steps, read by BOTH the content and
   the header above it.
   ══════════════════════════════════════════════════════════════════
   Root cause it fixes: `.page-content` stepped 32 -> 24 -> 16 while
   `.screen-header` hardcoded 32 with no media query, so on every screen
   whose header lives OUTSIDE .page-content (the .finance-shell family:
   finance / services / inventory) the title drifted from the cards
   below it — measured 8px at tablet, 16px at mobile. Half the screens
   aligned, half didn't; that IS the "titles sit in different places".
   Now both read this var, so they cannot drift again by construction.
   Steps match the breakpoints .page-content already used (1200 / 768),
   768 being the app's de-facto device line (~35 uses + the mobile shell
   + the bottom nav + .kpi-grid) and --bp-tablet in tokens.css.
   ══════════════════════════════════════════════════════════════════ */

:root { --screen-inset: 32px; }

@media (max-width: 1200px) { :root { --screen-inset: 24px; } }

@media (max-width: 768px)  { :root { --screen-inset: 16px; } }

.page-content {
  padding: 16px var(--screen-inset) 32px;
}

/* ══════════════════════════════════════════════════════════════════
   THE LAW: --screen-inset is applied EXACTLY ONCE per screen.
   ══════════════════════════════════════════════════════════════════
   Anything already inset by an ancestor content column does NOT inset
   itself again - otherwise the two silently ADD and that screen sits
   deeper than every other one. Measured before this rule (778px, inset
   24): whatsapp nested .page-content inside .page-content -> 48px, i.e.
   double, while its 13 siblings sat at 24. finance/services stacked the
   .finance-scroll shell's bespoke 14px the same way -> 38px.
   Every "why is this screen further in?" report traces back to a second
   application of the inset, so the fix lives here, once, for all of them.
   (`.page-content > .screen-header` is the same law for the header.)
   ══════════════════════════════════════════════════════════════════ */

.page-content .page-content {
  padding-inline: 0;
}

/* "The topbar is the header" (2026-07-06+, owner-approved placement): the
   ScreenActionBar row under the topbar hosts each screen's ONE primary action
   (RTL-left end) + an optional muted context string (RTL-right). Rendered by
   Router.jsx from AppContext.screenAction (set via useScreenAction). Slim -
   replaces the ~210px dash-hero. Horizontal padding matches .page-content so
   the action aligns with the content below it. Renders nothing when the screen
   declares no action. */

.screen-action-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 14px 32px 12px;
  border-bottom: 1px solid var(--border);
}

.screen-action-bar .sab-context {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-secondary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Owner note (2026-07-06): the action reads best on the RTL-left but pulled
   INWARD from the extreme edge, not stranded in the far corner. This inset
   scales with the viewport (bigger gap on wide desktops, small on phones). */

.screen-action-bar .btn { flex-shrink: 0; margin-inline-end: clamp(16px, 8vw, 120px); }

@media (max-width: 768px) {
  .screen-action-bar { padding: 12px 16px 10px; }
  .screen-action-bar .btn { margin-inline-end: 24px; }
}

/* ============================================
   Grid helpers
   ============================================ */

.grid { display: grid; gap: var(--space-4); }

.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }

.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }

.flex { display: flex; }

.flex.gap-8 { gap: var(--space-2); }

.flex.gap-12 { gap: var(--space-3); }

.flex.ai-c { align-items: center; }

.flex.jc-sb { justify-content: space-between; }

.flex.wrap { flex-wrap: wrap; }

/* ============================================
   Animations
   ============================================ */

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.96); }
  to { opacity: 1; transform: scale(1); }
}

/* accountingBackdropIn keyframe consolidated into backdropIn (V-Interaction/3, ADR-064) — identical opacity-only fade */

@keyframes accountingModalIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.975) translateY(12px); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1) translateY(0); }
}

.fade-in { animation: fadeIn 0.25s ease-out; }

.scale-in { animation: scaleIn var(--duration-base) var(--ease-out); }

.accounting-backdrop { animation: backdropIn var(--duration-base) var(--ease-out); }

.accounting-modal { animation: accountingModalIn var(--modal-in) var(--modal-in-ease) var(--modal-lead) backwards; will-change: transform, opacity; }

/* ============================================
   Modal — Glass Morphism Style
   ============================================ */

@keyframes modalIn {
  from { opacity: 0; transform: translateY(26px) scale(0.965); }
  to   { opacity: 1; transform: translateY(0)    scale(1);    }
}

@keyframes backdropIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.65);
  backdrop-filter: blur(var(--glass-blur-sm));
  -webkit-backdrop-filter: blur(var(--glass-blur-sm));
  display: grid;
  place-items: center;
  z-index: 100;
  animation: backdropIn var(--duration-base) var(--ease-out);
}

[data-theme="luxe"] .modal-backdrop,
[data-theme="midnight"] .modal-backdrop,
[data-theme-mode="dark"] .modal-backdrop { background: rgba(0, 0, 0, 0.55); }

/* Glass card — used by .modal AND .modal-box. Glass values come
   from tokens.css (--glass-bg / --glass-blur / --glass-border /
   --glass-shadow); dark themes auto-switch via [data-theme] overrides
   in tokens.css, so no per-theme block is needed here (ADR-017). */

.modal, .modal-box {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--glass-shadow);
  max-width: 600px;
  width: calc(100vw - 40px);
  max-height: calc(100dvh - 80px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: modalIn var(--modal-in) var(--modal-in-ease) var(--modal-lead) backwards;
}

.modal.lg, .modal-lg  { max-width: 760px; }

.modal.xl, .modal-xl  { max-width: 1100px; }

.modal-header {
  padding: 20px 24px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.07);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

[data-theme="luxe"] .modal-header,
[data-theme="midnight"] .modal-header,
[data-theme-mode="dark"] .modal-header { border-bottom-color: rgba(255,255,255,0.07); }

.modal-title { font-size: 18px; font-weight: 800; color: var(--text-primary); }

.modal-body { padding: var(--space-6); overflow-y: auto; flex: 1; }

.modal-footer {
  padding: 16px 24px;
  border-top: 1px solid rgba(0, 0, 0, 0.07);
  display: flex;
  gap: var(--space-2);
  justify-content: flex-start;
}

[data-theme="luxe"] .modal-footer,
[data-theme="midnight"] .modal-footer,
[data-theme-mode="dark"] .modal-footer { border-top-color: rgba(255,255,255,0.07); }

/* Accounting modals — override .card with glass tokens (ADR-017) */

.accounting-modal {
  background: var(--glass-bg) !important;
  backdrop-filter: blur(var(--glass-blur)) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) !important;
  border: 1px solid var(--glass-border) !important;
  box-shadow: var(--glass-shadow) !important;
  border-radius: var(--radius-xl) !important;
}

/* ============================================
   Glass Modal Utility — for inline-style modals
   Add className="gm-bd" to backdrop, "gm-box" to modal box
   ============================================ */

.gm-bd {
  background: rgba(0, 0, 0, 0.22) !important;
  backdrop-filter: blur(var(--glass-blur-sm)) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur-sm)) !important;
}

[data-theme="luxe"] .gm-bd,
[data-theme="midnight"] .gm-bd,
[data-theme-mode="dark"] .gm-bd { background: rgba(0, 0, 0, 0.55) !important; }

.gm-box {
  background: var(--glass-bg) !important;
  backdrop-filter: blur(var(--glass-blur)) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) !important;
  border: 1px solid var(--glass-border) !important;
  box-shadow: var(--glass-shadow) !important;
  border-radius: var(--radius-xl) !important;
  animation: modalIn var(--modal-in) var(--modal-in-ease) var(--modal-lead) backwards !important;
}

/* ============================================
   Tables
   ============================================ */

/* Anatomy W2 (2026-07-12): the ONE .data-table shell. Was 4 scattered
   blocks (here + ~3078 + ~5131 + ~5740) that partly cancelled each other
   (uppercase set then unset, tr transition ×3, border-collapse collapse
   then separate, header restyled twice). Merged into this single premium
   block so every table shares one look. Premium refinements: 600-weight
   muted header (no uppercase), sticky, hairline separators, consistent
   row rhythm. Row-accent stripes / selection / clickable hover-stripe /
   striped+compact+no-sticky modifiers stay in their own labelled blocks. */

.data-table {
  width: 100%;
  border-collapse: separate;   /* separate + spacing:0 keeps the sticky-header border intact */
  border-spacing: 0;
  font-size: var(--text-base);
}

.data-table thead th {
  text-align: start;
  padding: 8px 16px;
  position: sticky;
  top: 0;
  z-index: 1;
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text-tertiary);
  letter-spacing: 0;
  text-transform: none;
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-smooth);
}

.data-table tbody td {
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
  vertical-align: middle;
  line-height: 1.5;
  transition: background-color var(--duration-fast) var(--ease-smooth);
}

/* Hover Wave 2a/3b: ONE row-hover rule (-> --state-row-hover), guarded by
   @media (hover: hover) so touch devices (tap-emulated sticky :hover) never
   paint it and leave it stuck. */

.data-table tbody tr {
  position: relative;
  transition: background-color var(--duration-fast) var(--ease-smooth);
}

@media (hover: hover) {
.data-table tbody tr:hover { background: var(--state-row-hover); }
}

.data-table tbody tr:last-child td { border-bottom: none; }

.data-table .icon-btn {
  width: 34px;
  height: 34px;
  border-radius: 9px;
}

/* ============================================
   Tweaks panel
   ============================================ */

.tweaks-panel {
  position: fixed;
  bottom: 20px;
  inset-inline-start: 20px;
  width: 280px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-4);
  z-index: 1000;
  animation: scaleIn var(--duration-base) var(--ease-out);
}

.tweaks-panel h4 {
  font-size: var(--text-sm);
  font-weight: 800;
  color: var(--text-primary);
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.tweak-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
  padding: 8px 0;
}

.tweak-row .label-txt { font-size: var(--text-meta); color: var(--text-secondary); font-weight: 500; }

/* U-8: dev-only theme grid inside Tweaks panel (6 swatches) */

.tweak-theme-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-1-5);
}

.tweak-theme-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  padding: 6px 4px;
  background: transparent;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color var(--duration-fast), background var(--duration-fast), transform var(--duration-fast);
}

.tweak-theme-item:hover { background: var(--bg-hover); transform: translateY(var(--lift-1)); }

.tweak-theme-item.active { border-color: var(--accent); background: var(--bg-hover); }

.tweak-theme-item:focus-visible {
  outline: none;
  box-shadow: var(--ring-shadow);
}

.tweak-theme-swatch {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid rgba(0,0,0,.08);
  flex-shrink: 0;
}

.tweak-theme-label {
  font-size: 9px;
  font-weight: 600;
  color: var(--text-secondary);
  text-align: center;
  line-height: 1.2;
}

/* ============================================
   Utility
   ============================================ */

.text-xs { font-size: var(--text-xs); }

.text-sm { font-size: var(--text-sm); }

.text-lg { font-size: 16px; }

.text-xl { font-size: 20px; }

.text-secondary { color: var(--text-secondary); }

.text-tertiary { color: var(--text-tertiary); }

.mb-4 { margin-bottom: 4px; }

.mb-8 { margin-bottom: 8px; }

.mb-12 { margin-bottom: 12px; }

.mb-16 { margin-bottom: 16px; }

.mb-24 { margin-bottom: 24px; }

.mt-8 { margin-top: 8px; }

.mt-16 { margin-top: 16px; }

.mt-24 { margin-top: 24px; }

/* Anatomy W0-1: p-12 was used by inventory/variance cards but never defined -
   those cards rendered with NO padding from the class. */

.p-12 { padding: var(--space-3); }

.p-16 { padding: var(--space-4); }

.p-20 { padding: var(--space-5); }

.p-24 { padding: var(--space-6); }

.progress {
  height: 6px;
  background: var(--bg-subtle);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  background: var(--accent);
  border-radius: var(--radius-pill);
  transition: width var(--duration-slow) var(--ease-smooth);
}

.kbd {
  display: inline-block;
  padding: 2px 6px;
  font-size: var(--text-xs);
  font-family: 'JetBrains Mono', monospace;
  color: var(--text-tertiary);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
}

/* Login screen specific */

.empty-state {
  padding: 48px 24px;
  text-align: center;
  color: var(--text-tertiary);
}

.empty-state-icon {
  width: 56px;
  height: 56px;
  border-radius: 14px;
  background: var(--bg-subtle);
  display: grid;
  place-items: center;
  margin: 0 auto 16px;
  color: var(--text-muted);
}

/* Calendar */

.calendar-day {
  background: var(--bg-elevated);
  min-height: 90px;
  padding: var(--space-2);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-smooth);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.calendar-day:hover { background: var(--bg-hover); }

/* Appointment timeline */

.timeline {
  position: relative;
  padding-inline-end: 24px;
}

.timeline::before {
  content: "";
  position: absolute;
  inset-inline-end: 8px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: var(--border);
}

.timeline-item {
  position: relative;
  padding: 0 28px 20px 0;
}

.timeline-item::before {
  content: "";
  position: absolute;
  inset-inline-end: 4px;
  top: 6px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--bg-elevated);
}

/* .kpi / .card.kpi family removed (KpiCard P5, 2026-07-10): last consumer was
   SkeletonKpi, retired for SkeletonMetricTile. Use MetricTile. */

/* Toast */

.toast {
  position: fixed;
  bottom: 24px;
  inset-inline-end: 24px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 14px 18px;
  z-index: 2000;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  animation: scaleIn var(--duration-base) var(--ease-out);
  font-weight: 600;
  color: var(--text-primary);
  font-size: var(--text-base);
  max-width: 360px;
}

.toast.success { border-color: var(--success); }

.toast-icon {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  background: var(--success-soft);
  color: var(--success);
  flex-shrink: 0;
}

/* Tabs */

.tabs {
  display: flex;
  gap: var(--space-1);
  border-bottom: 1px solid var(--border);
  margin-bottom: 20px;
  overflow-x: auto;
}

/* F5 (2026-07-09): billing status filter tabs render the shared Tabs primitive
   but keep the screen's inline padding + no bottom gap (the hand-rolled strip
   they replaced sat flush). */

.tabs.billing-status-tabs { margin-bottom: 0; padding-inline: 16px; }

/* Sticky screen tabs are pinned at the WRAPPER level, uniformly, so every
   tabbed screen behaves the same (owner 2026-07-06 "حل معماري واحد"):
     - patient-file  -> .pf-tab-strip           (pre-existing sticky)
     - finance/services/inventory -> .finance-tabs-wrap  (sticky)
     - reception     -> its inline tab wrapper  (sticky)
   position:sticky pins relative to WHATEVER scroller is active (.main-content
   on desktop, document.body on mobile, or an inner box) and never touches the
   model-agnostic pull-to-refresh. No per-.tabs sticky needed. */

.tab {
  padding: 10px 16px;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-tertiary);
  cursor: pointer;
  border: none;
  background: transparent;
  border-bottom: 2px solid transparent;
  transition: all var(--duration-fast) var(--ease-smooth);
  white-space: nowrap;
  font-family: inherit;
  margin-bottom: -1px;
}

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

.tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.app-shell {
  flex: 1;
  width: 100%;
  height: 100%;
  display: flex;
}

.main {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
  /* min-height:0 lets .main-content shrink below its content and scroll inside
     this bounded flex column (the nested-flex overflow gotcha). */
  min-height: 0;
}

.main-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* Momentum scroll on older iOS; harmless elsewhere. This is the single
     scroll surface for the authed app on every breakpoint + platform. */
  -webkit-overflow-scrolling: touch;
  /* Navigation Wave 2: disables the browser's own reload-on-pull on this
     scroller. The pull-to-refresh gesture (lib/pull-to-refresh.js) is fully
     PASSIVE and never fights scroll, so it relies on this CSS. */
  overscroll-behavior-y: contain;
}

/* ══════════════════════════════════════════════════════════════════════
   Mobile Shell Wave 1 (step A) — bottom tab bar + --bottom-nav-height token
   ══════════════════════════════════════════════════════════════════════
   The bar (`app/shell/mobile-bottom-nav.jsx`) is a flex sibling INSIDE
   `.main` AFTER `.main-content`, so the scroller shrinks to leave room for
   it (no fixed-overlay + content padding hack). Desktop: display:none and
   the token is 0 -> floating elements (chat dock / AI ball / PTR) sit at
   their normal offset. <=768px: the token becomes the bar height (incl. iOS
   safe area) so those SAME floating elements clear the bar through ONE
   variable. VITE_MODIRIFY_MOBILE_SHELL=0 makes the component render null. */

:root { --bottom-nav-height: 0px; }

.mobile-bottom-nav { display: none; }

@media (max-width: 768px) {
  :root { --bottom-nav-height: calc(58px + env(safe-area-inset-bottom, 0px)); }

  .mobile-bottom-nav {
    display: flex;
    align-items: stretch;
    flex-shrink: 0;
    height: var(--bottom-nav-height);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--bg-elevated);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.06);
    z-index: 50;
    transition: transform 220ms var(--ease-smooth), margin-bottom 220ms var(--ease-smooth);
  }
  /* Hide-on-scroll (owner 2026-07-18): the bottom bar rides the SAME
     `.chrome-hidden` signal the topbar and the screen tab bar already use —
     set by lib/screen-tab-scroll.js from the scroll DIRECTION. No new JS and
     no second scroll listener, so the three chrome layers can never disagree
     about whether the user is scrolling down. Mirrors the topbar recipe: slide
     out, then give the reclaimed height back to the content with a negative
     margin (the bar is a flex child of `.main`, not a fixed overlay). */
  .main.chrome-hidden .mobile-bottom-nav {
    transform: translateY(100%);
    margin-bottom: calc(var(--bottom-nav-height) * -1);
  }
  .mbn-tab {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 7px 2px 5px;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-tertiary);
    font-family: inherit;
    transition: color var(--duration-fast, 0.15s) var(--ease-smooth, ease);
    -webkit-tap-highlight-color: transparent;
  }
  .mbn-tab svg { transition: transform var(--duration-base, 0.2s) var(--ease-smooth, ease); }
  .mbn-label {
    font-size: 10.5px;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .mbn-tab.active { color: var(--accent); }
  .mbn-tab.active svg { transform: scale(1.08); }
  /* premium active indicator: a short accent bar hugging the top edge */
  .mbn-tab.active::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 26px;
    height: 3px;
    border-radius: 0 0 4px 4px;
    background: var(--accent);
  }
  .mbn-tab:active { transform: scale(0.93); }

  /* ────────────────────────────────────────────────────────────────────
     ADR-288 — MobileActionFab: the screen's ONE primary action, docked.
     ────────────────────────────────────────────────────────────────────
     The slot is a REAL flex child of the bar, not an overlay. That is the
     whole no-overlap guarantee: the tabs are `flex: 1 1 0`, so they divide
     only the width the slot leaves behind and can never end up underneath
     the button. (The first attempt floated a labelled pill over a bar whose
     tabs still spanned the full width — it sat on top of "الفواتير" and
     "المالية". Owner, 2026-07-25: "شكله وحش ومغطى على اسامي السايدربار".)
     The pill is a CHILD of the slot, so it stays centred in its own reserved
     space even when a restricted account renders fewer tabs.
     --mfab-lift: how far it rises while naming itself. `.mbn-label` starts
     34px below the bar's top edge, so 26px clears it with room to spare —
     the expanded pill never touches a label either. */
  .mfab-slot {
    position: relative;
    flex: 0 0 70px;
    min-width: 0;
    --mfab-lift: 26px;
  }
  .mfab {
    position: absolute;
    left: 50%;
    bottom: 6px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0;
    height: 46px;
    width: auto;
    min-width: 46px;
    /* Collapsed = a circle. `max-width` (not `width`) is what animates, so the
       box can grow to its INTRINSIC width and stop there instead of snapping. */
    max-width: 46px;
    padding: 0;
    border: none;
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: #fff;
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 750;
    white-space: nowrap;
    overflow: hidden;
    cursor: pointer;
    z-index: 5;
    -webkit-tap-highlight-color: transparent;
    box-shadow:
      0 6px 18px -4px color-mix(in srgb, var(--accent) 55%, transparent),
      0 2px 6px -1px color-mix(in srgb, var(--accent) 40%, transparent);
    transition:
      max-width var(--duration-slow, 0.36s) var(--ease-smooth, ease),
      padding var(--duration-slow, 0.36s) var(--ease-smooth, ease),
      gap var(--duration-slow, 0.36s) var(--ease-smooth, ease),
      transform var(--duration-slow, 0.36s) var(--ease-smooth, ease),
      background var(--duration-base, 0.2s) var(--ease-smooth, ease),
      box-shadow var(--duration-base, 0.2s) var(--ease-smooth, ease);
  }
  .mfab-glyph { display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
  .mfab-label {
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    transition:
      max-width var(--duration-slow, 0.36s) var(--ease-smooth, ease),
      opacity var(--duration-base, 0.2s) var(--ease-smooth, ease);
  }
  /* The announce moment: rise clear of the labels, widen to the measured
     intrinsic width (--mfab-open, set per label by the component), hold, then
     settle back to a circle. It fires only when the action itself CHANGES. */
  .mfab.is-announcing {
    max-width: var(--mfab-open, 200px);
    padding: 0 16px;
    gap: 7px;
    transform: translateX(-50%) translateY(calc(var(--mfab-lift) * -1));
    box-shadow:
      0 12px 26px -6px rgba(0, 0, 0, 0.28),
      0 6px 18px -4px color-mix(in srgb, var(--accent) 55%, transparent);
  }
  .mfab.is-announcing .mfab-label { max-width: 220px; opacity: 1; }
  /* The measuring state: the pill's OPEN shape with no width cap, so the
     component can ask the browser for its true intrinsic width instead of
     restating padding/gap/icon-size as a constant in JS (which drifted by 2px
     and clipped the last Arabic letter). Applied and removed inside one layout
     effect, so it is never painted. */
  .mfab.is-measuring { max-width: none; padding: 0 16px; gap: 7px; transition: none; }
  .mfab.is-measuring .mfab-label { max-width: none; opacity: 1; transition: none; }
  /* No primary action on this screen/tab -> muted, opens the command palette.
     It must never DISAPPEAR: a control that vanishes under the thumb changes
     the bar's contents mid-reach and reads as a bug. */
  .mfab.is-quiet {
    background: var(--bg-elevated);
    color: var(--text-secondary);
    border: 1px solid var(--border);
    box-shadow: 0 3px 12px -2px rgba(0, 0, 0, 0.14);
  }
  .mfab:disabled { opacity: 0.55; cursor: default; }
  .mfab:active:not(:disabled) { transform: translateX(-50%) scale(0.93); }
  .mfab.is-announcing:active:not(:disabled) {
    transform: translateX(-50%) translateY(calc(var(--mfab-lift) * -1)) scale(0.93);
  }
  .mfab:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
  .mfab-glyph { animation: mfabGlyphIn var(--duration-slow, 0.36s) var(--ease-smooth, ease); }
  @keyframes mfabGlyphIn {
    0%   { transform: rotate(-60deg) scale(0.35); opacity: 0; }
    60%  { transform: rotate(5deg) scale(1.08); opacity: 1; }
    100% { transform: rotate(0) scale(1); opacity: 1; }
  }

  /* #7: hide the content scrollbars on touch. MODEL-AGNOSTIC: the authed
     scroller is `.main-content` for simple screens, but tabbed screens
     (reception / finance / analytics) keep their tab strip fixed and scroll an
     INNER overflow:auto box — so hide BOTH `.main-content` AND every
     descendant's scrollbar (mirrors the model-agnostic scroll-to-top). Mobile
     browsers auto-hide them anyway; this keeps the premium feel on the Android
     WebView + emulated mobile. */
  .main-content,
  .main-content * { scrollbar-width: none; }
  .main-content::-webkit-scrollbar,
  .main-content *::-webkit-scrollbar { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .mobile-bottom-nav { transition: none; }
  .mbn-tab, .mbn-tab svg { transition: none; }
  .mbn-tab:active { transform: none; }
  /* ADR-288: the pill still NAMES itself (that is information, not decoration)
     — it just stops travelling to do it. No lift, no glyph spin, no easing. */
  .mfab, .mfab-label { transition: none; }
  .mfab-glyph { animation: none; }
  .mfab.is-announcing { transform: translateX(-50%); }
  .mfab.is-announcing:active:not(:disabled) { transform: translateX(-50%); }
  .mfab:active:not(:disabled) { transform: translateX(-50%); }
}

/* ══════════════════════════════════════════════════════════════════════
   Mobile Shell Wave 1 step B — theme-in-sidebar (#6) + drawer safe-area (#5)
   + route slide (#4)
   ══════════════════════════════════════════════════════════════════════ */

/* #6 RETIRED (Chrome round 3, owner 2026-08-13). The mobile-only collapsible
   swatch block in the nav sheet is gone, with its reveal keyframes. It only
   ever existed because the topbar appearance button was hidden on phones; the
   owner asked for that button back, so this was a second, heavier door onto
   one preference. Appearance + language are ONE topbar popover now, on every
   breakpoint (see .theme-picker-drop and the .topbar-theme rules). */

/* #5: the clinic-room native safe-area fix MOVED (owner 2026-07-18). The
   bespoke `.crd-drawer` is gone — the Clinic Room now shares the central
   ChatModal with WhatsApp and Modir, so the status-bar / home-indicator
   handling lives ONCE on `body.is-native .rm-card.chat-modal` (search
   "Native (Capacitor) safe areas") and covers all three chats instead of one. */

/* #4: subtle slide-in on screen change (MOBILE only). A cheap transform + fade
   (~200ms) so navigation feels continuous, not a hard cut. Desktop keeps the
   instant settle (ADR-143). key=currentScreen (Router.jsx) restarts it per nav.
   Respects reduced-motion. */

@media (max-width: 768px) {
  .route-slide {
    animation: routeSlideIn 200ms var(--ease-out, cubic-bezier(0.22, 0.61, 0.36, 1)) both;
  }
}

@keyframes routeSlideIn {
  from { opacity: 0; transform: translateX(18px); }
  to   { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .route-slide { animation: none; }
}

/* Mobile Shell #3 swipe transition: NO CSS keyframe. The pane follows the finger
   1:1 and settles home entirely via a JS transform transition owned by the
   detector (lib/tab-swipe.js settlePane) so the new content rides one continuous
   slide from the finger's release offset -- a keyframe here would fight it. */

/* Premium tab polish (Mobile Shell #3 follow-up, 2026-07-05). The shared
   underline tab model stays (every tabbed screen uses `.tab`), but the active +
   hover states get a modern treatment: rounded top corners, a soft hover fill,
   a whisper-light accent tint on the active tab, and a rounded INSET accent bar
   instead of a flat full-width line. `.tabs .tab` is already position:relative,
   so the bar is an ::after that grows in. NOTE: the active FILL avoids
   `--accent-soft` on purpose — the count badge uses that token, so a same-token
   fill would swallow it. NO edge gradients / chevrons (ADR-140 W6 retired). */

.tab {
  border-radius: 10px 10px 0 0;
}

@media (hover: hover) {
.tabs .tab:hover:not(.active) {
  background: var(--bg-hover);
  /* Hover Wave 2a: the color half of this hover lived in a second
     identical selector in the premium section - merged here. */
  color: var(--text-secondary);
}
}

.tab.active {
  border-bottom-color: transparent;   /* the flat line is replaced by the inset bar */
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.tab.active::after {
  content: '';
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 0;   /* flush with the tab bottom -- no protrusion (.tabs overflow-y computes to auto) */
  height: 3px;
  border-radius: 3px 3px 0 0;
  background: var(--accent);
  animation: tabBarIn var(--duration-fast, 160ms) var(--ease-out, cubic-bezier(0.22, 0.61, 0.36, 1));
}

@keyframes tabBarIn { from { transform: scaleX(0.4); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }

@media (prefers-reduced-motion: reduce) { .tab.active::after { animation: none; } }

/* Pull-to-refresh indicator (Navigation Wave 2) ----------------------------
   One fixed pill owned by lib/pull-to-refresh.js (passive gesture). Hidden by
   default; the controller drives transform + opacity as the finger pulls the
   scroller down at the top, then spins on release. Touch devices only. */

.ptr-indicator {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 1500;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg-elevated);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translate(-50%, -44px);
  pointer-events: none;
  will-change: transform, opacity;
}

.ptr-indicator.ptr-return {
  transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.3s ease;
}

.ptr-indicator.is-armed {
  box-shadow: 0 0 0 2px var(--accent), var(--shadow-lg);
}

.ptr-spinner {
  width: 22px;
  height: 22px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
}

.ptr-indicator.is-spinning .ptr-spinner {
  animation: spin 0.7s linear infinite;
}

/* Mobile gets pull-to-refresh (a passive global gesture), so the redundant
   topbar refresh button is hidden at the topbar's own mobile breakpoint --
   the same 47.99em line the sidebar/hamburger split uses. Width-based, NOT
   `pointer: coarse`: the media-feature query did not match reliably in Chrome
   DevTools device emulation (nor some webviews), so the button never hid.
   Desktop (>47.99em) keeps the manual refresh button. The back arrow was
   removed from the topbar markup entirely -- see topbar.jsx. */

@media (max-width: 47.99em) {
  .topbar-refresh {
    display: none;
  }
  /* Wave C: drop the title icon square on phones so the screen name keeps
     room next to the action cluster (the lang toggle stays - it has no
     mobile alternative). */
  .topbar-title-icon {
    display: none;
  }
}

@media (max-width: 1200px) {
  .sidebar {
    width: 224px;
  }
  .page-header {
    padding: 20px 24px 8px;
  }
  .page-content {
    /* W-HDR: horizontal via --screen-inset (24px here) so the header
       above tracks it; the vertical steps stay per-breakpoint. */
    padding: 14px var(--screen-inset) 24px;
  }
  .grid.cols-4 {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 1024px) {
  .topbar {
    padding: 0 16px;
    gap: var(--space-3);
  }
  .topbar .search {
    max-width: 320px;
  }
  .page-title {
    font-size: var(--text-title);
  }
  .grid.cols-4 {
    grid-template-columns: repeat(2, 1fr);
  }
  .modal {
    width: calc(100vw - 24px);
    max-height: calc(100dvh - 32px);
  }
  .modal-header,
  .modal-body,
  .modal-footer {
    padding-inline: 18px;
  }
  .data-table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
    -webkit-overflow-scrolling: touch;
  }
}

@media (max-width: 768px) {
  /* MOBILE SCROLL MODEL (2026-07-04 architectural fix): the whole app uses ONE
     scroll model on EVERY breakpoint + platform -- the shell is a fixed 100dvh
     flex column and `.main-content` owns the scroll (the base `#root` +
     `.main-content` rules already define this). The old override that lived
     here (`html,body{overflow:auto}` + shell `height:auto` -> the PAGE/body
     became the scroller) was the ROOT CAUSE of dead single-finger scroll on
     mobile browsers AND the native app, and has been REMOVED. There is no
     longer an override fighting the base model. */
  /* U-7/2 — the desktop sidebar is hidden on mobile; navigation moves
     into the MobileNav bottom-sheet (opened by the topbar hamburger). */
  .app-shell > .sidebar {
    display: none;
  }
  .topbar {
    height: auto;
    padding: 12px 16px;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2-5);
  }
  /* Hamburger first, title block takes remaining width on the same row */
  .topbar > .topbar-hamburger {
    order: 0;
    flex-shrink: 0;
  }
  .topbar > .topbar-title-block {
    order: 1;
    flex: 1;
    min-width: 0;
  }
  .topbar .search {
    order: 3;
    width: 100%;
    max-width: none;
  }
  .topbar-actions {
    order: 4;
    width: 100%;
    margin-inline-start: 0;
    justify-content: flex-start;
    flex-wrap: wrap;
  }
  .page-header {
    padding: 16px 16px 8px;
    align-items: flex-start;
  }
  .page-title {
    font-size: 20px;
  }
  .page-subtitle {
    font-size: var(--text-meta);
  }
  .page-content {
    /* W-HDR: horizontal via --screen-inset (16px here). */
    padding: 12px var(--screen-inset) 20px;
  }
  .grid.cols-2,
  .grid.cols-4 {
    grid-template-columns: 1fr;
  }
  .modal-header,
  .modal-body,
  .modal-footer {
    padding: var(--space-4);
  }
  .toast {
    inset-inline: 12px;
    bottom: 12px;
    max-width: none;
  }
  .tweaks-panel {
    width: calc(100vw - 24px);
    inset-inline-start: 12px;
    bottom: 72px;
  }
}

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

/* idea #2 — one-time pulse ring on the topbar "watch this screen" help button.
   A ::after ring (var(--accent), dark-mode safe) scales + fades out. The button
   sets position:relative inline; the ring is pointer-events:none so it never
   blocks the click. Only present while .topbar-help-pulse is on (until the user
   opens the help once). */

.topbar-help-pulse::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  opacity: 0;
  pointer-events: none;
  animation: topbarHelpPulse 1.8s ease-out infinite;
}

@keyframes topbarHelpPulse {
  0%   { opacity: .55; transform: scale(.85); }
  70%  { opacity: 0;   transform: scale(1.55); }
  100% { opacity: 0;   transform: scale(1.55); }
}

/* idea #2 step 2 — مركز الشرح (learning library) cards in the support screen.
   Responsive auto-fit grid; each card is a YouTube thumbnail with a play
   overlay + a title below. All-plans (no gating). */

.help-lib-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3);
}

.help-lib-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg-elevated);
  cursor: pointer;
  overflow: hidden;
  text-align: start;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}

.help-lib-card:hover {
  border-color: var(--accent);
  transform: translateY(var(--lift-2));
  box-shadow: var(--shadow-md);
}

.help-lib-card__thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--bg-subtle);
  overflow: hidden;
}

.help-lib-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.help-lib-card__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: rgba(0, 0, 0, .28);
  transition: background .15s ease;
}

.help-lib-card:hover .help-lib-card__play { background: rgba(0, 0, 0, .12); }

.help-lib-card__title {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.4;
  padding: 0 10px 12px;
}

/* Clinic Room (ADR-197) Wave 4 — the "nudge/buzz" icon shake. Applied to the
   topbar chat icon for ~0.6s when an incoming nudge arrives. */

@keyframes cr-nudge-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-4px) rotate(-6deg); }
  30% { transform: translateX(4px) rotate(6deg); }
  45% { transform: translateX(-3px) rotate(-4deg); }
  60% { transform: translateX(3px) rotate(4deg); }
  75% { transform: translateX(-2px); }
}

.cr-nudge-shake { animation: cr-nudge-shake 0.6s ease-in-out; }

/* ============================================================
   Kanban Board — العيادة اليوم
   ============================================================ */

.kanban-board {
  display: grid;
  /* 6 stages since 2026-07-18 (cancelled got its own column). */
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-2-5);
  overflow-x: auto;
  padding-bottom: 8px;
  align-items: start;
}

.kanban-col {
  min-width: 150px;
  max-width: 220px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.kanban-col-hdr {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 700;
  font-size: var(--text-meta);
  background: var(--bg-elevated);
}

.kanban-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 9px;
  background: var(--bg-elevated);
  transition: box-shadow var(--duration-base) var(--ease-smooth), transform var(--duration-base) var(--ease-smooth);
  cursor: default;
  font-size: var(--text-meta);
}

/* Hover Wave 2b: the kanban-card hover that lived here was one of THREE
   stacked .kanban-card:hover rules (this + premium + redesign) where the
   cascade already let the last one win per-property. ONE rule now lives
   in the kanban redesign section with the exact effective values. */

.kanban-empty {
  text-align: center;
  color: var(--text-tertiary);
  padding: 16px 6px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
}

.wa-template-preview {
  background: #dcfce7;
  border: 1px solid #bbf7d0;
  border-radius: 12px 12px 12px 4px;
  padding: 14px 16px;
  font-size: var(--text-sm);
  line-height: 1.8;
  color: #065f46;
  white-space: pre-line;
}

.wa-template-preview::before {
  content: 'نص الرسالة';
  display: block;
  font-size: var(--text-micro);
  font-weight: 700;
  color: #16a34a;
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-bottom: 8px;
}

.wa-stat {
  border: 1px solid var(--border);
  background: var(--bg-subtle);
  border-radius: var(--radius);
  padding: 12px 14px;
}

.wa-stat-label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  margin-bottom: 6px;
}

.wa-stat-value {
  font-size: var(--text-title);
  font-weight: 800;
  color: var(--text-primary);
}

.wa-template-editor {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: var(--space-3);
}

@media (max-width: 900px) {
  .wa-template-editor {
    grid-template-columns: 1fr;
  }
}

/* ============================================================
   Lab Orders
   ============================================================ */

/* Wave 2a (V-Patients/4) — patient rows with outstanding debt get a
   subtle red leading edge. The .patients-table mobile-card sibling
   inherits the same accent via .dt-card.patient-row-debt rule below. */

.data-table tbody tr.patient-row-debt > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--danger);
}

[dir="rtl"] .data-table tbody tr.patient-row-debt > td:first-child {
  box-shadow: inset -3px 0 0 0 var(--danger);
}

.dt-card.patient-row-debt {
  border-inline-start: 3px solid var(--danger);
}

/* Shared settlement rows — invoices, expenses, purchases, and lab orders use
   the same paid/partial/unpaid leading edge on desktop and mobile. */

.data-table tbody tr.settlement-row--paid    > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--success);
}

.data-table tbody tr.settlement-row--partial > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--accent);
}

.data-table tbody tr.settlement-row--unpaid  > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--danger);
}

[dir="rtl"] .data-table tbody tr.settlement-row--paid    > td:first-child {
  box-shadow: inset -3px 0 0 0 var(--success);
}

[dir="rtl"] .data-table tbody tr.settlement-row--partial > td:first-child {
  box-shadow: inset -3px 0 0 0 var(--accent);
}

[dir="rtl"] .data-table tbody tr.settlement-row--unpaid  > td:first-child {
  box-shadow: inset -3px 0 0 0 var(--danger);
}

.dt-card.settlement-row--paid    { border-inline-start: 3px solid var(--success); }

.dt-card.settlement-row--partial { border-inline-start: 3px solid var(--accent); }

.dt-card.settlement-row--unpaid  { border-inline-start: 3px solid var(--danger); }

/* Wave 2d (V-Inventory/5) — inventory rows get a leading edge keyed
   to stock status: out=danger / low=warning. Available rows stay
   neutral (no border). Applied to both desktop table row first-cell
   AND mobile card border-inline-start. */

.data-table tbody tr.inv-row--out > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--danger);
}

.data-table tbody tr.inv-row--low > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--warning);
}

[dir="rtl"] .data-table tbody tr.inv-row--out > td:first-child {
  box-shadow: inset -3px 0 0 0 var(--danger);
}

[dir="rtl"] .data-table tbody tr.inv-row--low > td:first-child {
  box-shadow: inset -3px 0 0 0 var(--warning);
}

.dt-card.inv-row--out { border-inline-start: 3px solid var(--danger); }

.dt-card.inv-row--low { border-inline-start: 3px solid var(--warning); }

/* ============================================================
   Bulk row selection (ADR-208) — checkbox column + selected-row
   highlight, shared by every selectable DataTable. Mobile card gets
   the checkbox inside the card head.
   ============================================================ */

.data-table .dt-select-col {
  width: 40px;
  text-align: center;
  padding-inline: 8px;
}

.data-table .dt-select-col input[type="checkbox"],
.dt-card-select input[type="checkbox"] {
  width: 18px;
  height: 18px;
  cursor: pointer;
  accent-color: var(--accent);
  margin: 0;
}

.data-table tbody tr.dt-row--selected > td {
  background: color-mix(in srgb, var(--accent) 9%, transparent);
}

.dt-card-select {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  padding-inline-end: 4px;
}

.dt-card.dt-card--selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* Hover-reveal selection checkboxes (owner 2026-07-12): on desktop tables the
   checkbox column stays for layout stability, but each box is invisible at rest
   and fades in on row hover, on keyboard focus, when its row IS selected, or once
   ANY row is selected (dt-has-selection - keeps multi-select easy). Opt-in via
   the `dt-select-reveal` class (patients + billing). Mobile card selection is
   gated separately by selectableMobile, so this desktop-only reveal never hides
   a box the phone needs. */

.data-table.dt-select-reveal .dt-select-col input[type="checkbox"] {
  opacity: 0;
  transition: opacity .15s ease;
}

.data-table.dt-select-reveal tr:hover > .dt-select-col input[type="checkbox"],
.data-table.dt-select-reveal .dt-select-col input[type="checkbox"]:focus-visible,
.data-table.dt-select-reveal tbody tr.dt-row--selected > .dt-select-col input[type="checkbox"],
.data-table.dt-select-reveal.dt-has-selection .dt-select-col input[type="checkbox"] {
  opacity: 1;
}

/* ============================================================
   UI Consistency Pass
   Shared visual polish only. Keep behavior and data contracts intact.
   ============================================================ */

:where(.btn, .icon-btn, .nav-item, .tab, .input, .select, .textarea) {
  -webkit-tap-highlight-color: transparent;
}

.btn {
  min-height: 42px;
  line-height: 1.25;
}

.btn.sm {
  min-height: 34px;
}

.btn.icon {
  min-width: 40px;
  min-height: 40px;
}

.btn svg,
.icon-btn svg,
.nav-item svg {
  flex-shrink: 0;
}

.btn:disabled,
.icon-btn:disabled,
.nav-item:disabled {
  opacity: .55;
  cursor: not-allowed;
  box-shadow: none;
}

/* U-10/2: legacy outline-based focus rule removed. Canonical
   --ring-shadow rule now lives at line ~4573 (Unified focus rings)
   and explicitly includes .icon-btn + .nav-item. */

.input,
.select,
.textarea {
  min-height: var(--field-h);   /* Sizing W9: adopt token (was 42px -> 40) so every form field derives from one place */
  line-height: 1.45;
}

.textarea {
  min-height: 92px;
  resize: vertical;
}

.input:disabled,
.select:disabled,
.textarea:disabled {
  background: var(--bg-subtle);
  color: var(--text-tertiary);
  cursor: not-allowed;
  opacity: 1;
}

.card {
  transition: border-color var(--duration-fast) var(--ease-smooth), box-shadow var(--duration-fast) var(--ease-smooth), background-color var(--duration-fast) var(--ease-smooth);
}

.card:focus-within {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-soft) 80%, transparent);
}

.page-title {
  line-height: 1.25;
  letter-spacing: 0;
}

.page-subtitle {
  line-height: 1.6;
}

.page-header > *,
.topbar > * {
  min-width: 0;
}

.page-header .flex {
  flex-wrap: wrap;
}

.nav-item {
  line-height: 1.35;
}

.nav-item > span:not(.icon-box) {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* (.data-table base + thead + icon-btn consolidated into the ONE Tables
   shell above — 2026-07-12. This block used to UNSET the uppercase the
   base set, a classic cascade duplication.) */

.tabs {
  gap: var(--space-1-5);
  scrollbar-width: thin;
}

.tab {
  min-height: 42px;
  line-height: 1.35;
  white-space: nowrap;
}

.empty-state {
  min-height: 180px;
  display: grid;
  place-items: center;
  align-content: center;
  gap: var(--space-2);
  line-height: 1.6;
}

.empty-state-icon {
  box-shadow: inset 0 0 0 1px var(--border);
}

.toast,
.modal-title {
  line-height: 1.4;
}

.modal-body {
  overscroll-behavior: contain;
}

.modal-footer {
  flex-wrap: wrap;
}

.chip {
  line-height: 1.35;
}

.calendar-day {
  min-width: 0;
}

@media (max-width: 768px) {

  .btn {
    min-height: 40px;
    padding: 9px 13px;
  }

  .data-table thead th,
  .data-table tbody td {
    padding: var(--space-3);
  }

  .calendar-day {
    min-height: 78px;
  }

  .modal-footer .btn {
    flex: 1 1 120px;
  }
}

@media (max-width: 520px) {
  .page-header .flex,
  .topbar-actions {
    gap: var(--space-1-5);
  }

  .chip {
    max-width: 100%;
    white-space: normal;
  }

  .calendar-day {
    min-height: 70px;
    padding: var(--space-1-5);
  }
}

/* ============================================================
   MARKETING SCREEN — legacy .mk-* rules (tokens only, all themes)
   R2 W3 purge (2026-07-10) deleted the dead families (hero, steps,
   sections, assets, empty maps...); what survives is consumed by the
   live JSX: connect banner, calendar, image states, upgrade modal.
   New marketing styling belongs in marketing-redesign.css (.mkx-*).
   ============================================================ */

/* .mk-kpi / .mk-kpis KPI strip removed (KpiCard unification Phase 1, 2026-07-09): dead family, 0 JSX consumers. Use MetricTile. */

/* -- Connection banner --------------------------------------- */

.mk-connect {
  margin-top: 14px; padding: 14px 16px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--bg-subtle);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap;
}

.mk-connect.is-on {
  background: var(--success-soft);
  border-color: color-mix(in oklab, var(--success) 30%, var(--border));
}

.mk-connect.is-off { border-color: color-mix(in oklab, var(--warning) 30%, var(--border)); }

.mk-connect-title { font-weight: 900; color: var(--text-primary); display: flex; align-items: center; gap: var(--space-2); }

.mk-connect-status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--warning); box-shadow: 0 0 0 4px color-mix(in oklab, var(--warning) 18%, transparent);
}

.mk-connect.is-on .mk-connect-status-dot {
  background: var(--success); box-shadow: 0 0 0 4px color-mix(in oklab, var(--success) 18%, transparent);
}

.mk-connect-pages { display: flex; gap: var(--space-1-5); flex-wrap: wrap; margin-top: 8px; width: 100%; }

.mk-connect-page {
  font-size: var(--text-meta); padding: 5px 10px; border-radius: var(--radius-pill);
  background: var(--bg-card); border: 1px solid var(--border);
  color: var(--text-secondary);
}

/* -- Calendar ------------------------------------------------- */

.mk-image-empty {
  min-height: 220px; padding: 18px;
  border: 1px dashed color-mix(in oklab, var(--accent) 45%, var(--border));
  border-radius: var(--radius);
  background:
    radial-gradient(120% 80% at 0% 0%, color-mix(in oklab, var(--accent) 14%, transparent), transparent 60%),
    var(--bg-subtle);
  display: flex; flex-direction: column; gap: var(--space-2-5);
}

.mk-image-empty-title {
  display: flex; align-items: center; gap: var(--space-2);
  color: var(--accent-text); font-weight: 900;
}

.mk-image-empty-prompt { color: var(--text-secondary); line-height: 1.7; font-size: var(--text-sm); }

/* -- Reason / hint blocks ------------------------------------ */

.mk-reason {
  margin-top: 12px; padding: 12px 14px; border-radius: var(--radius);
  background: var(--accent-soft); color: var(--text-secondary);
  line-height: 1.7; font-size: var(--text-sm);
  border-inline-start: 3px solid var(--accent);
}

.mk-error-banner {
  margin-top: 10px; padding: 12px 14px; border-radius: var(--radius);
  background: var(--danger-soft); color: var(--danger);
  font-weight: 800; font-size: var(--text-sm); line-height: 1.7;
  border-inline-start: 3px solid var(--danger);
}

.mk-spin { animation: spin 1s linear infinite; }

/* -- Brand identity ------------------------------------------ */

/* .mk-brand-grid deleted W2-C3 (marketing-brand-drawer.jsx was removed). */

/* MKT-W3h: the ONE logo control in the composer. The preview is a real
   checkerboard so a NON-transparent upload is obvious before it ships on
   a month of images. */

.mkx-logo-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.mkx-logo-preview {
  width: 72px; height: 48px; border-radius: 8px; border: 1px solid var(--border);
  display: grid; place-items: center; padding: 4px; background-color: var(--bg-card);
  background-image:
    linear-gradient(45deg, var(--bg-subtle) 25%, transparent 25%),
    linear-gradient(-45deg, var(--bg-subtle) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--bg-subtle) 75%),
    linear-gradient(-45deg, transparent 75%, var(--bg-subtle) 75%);
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0;
}

.mkx-logo-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }

.mk-color-pick {
  display: grid; grid-template-columns: 44px 1fr; gap: var(--space-2);
}

.mk-color-pick input[type="color"] {
  width: 44px; height: 40px; padding: 2px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-card); cursor: pointer;
}

/* ============================================================
   Marketing — legacy .mk2-* rules (Concept V2, tokens only, RTL-first)
   R2 W3 purge (2026-07-10) deleted the dead zones (hero, action
   center, board, timeline, readiness, credit, setup...); what
   survives is consumed by the live JSX: confirm-generate modal +
   post drawer detail/meta + workspace card.
   ============================================================ */

/* .mk2-shell retired (R3 Phase 3, 2026-07-10): its last consumer was the
   marketing full-screen takeover loader - the screen root is
   .page-content.reveal-stagger like every other screen. */

/* ADR-075 §3: Pre-generation confirm modal — cost transparency before
   the AI call commits credits. Three actions (All / Text only / Cancel)
   stack vertically on mobile, row on desktop. */

.mk2-confirm-body{display:flex;flex-direction:column;gap:14px;padding:var(--space-1);}

.mk2-confirm-headline{
  font-size:var(--text-md);font-weight:800;color:var(--text-primary);line-height:1.5;
}

.mk2-confirm-costs{
  display:flex;flex-direction:column;gap:var(--space-1-5);
  padding:12px 14px;border-radius:var(--radius-sm);
  background:var(--bg-subtle);border:1px solid var(--border-subtle, var(--border));
}

.mk2-confirm-cost-row{
  display:flex;align-items:center;gap:var(--space-2);
  font-size:var(--text-sm);color:var(--text-secondary);
}

.mk2-confirm-cost-row strong{
  font-weight:900;color:var(--text-primary);font-variant-numeric:tabular-nums;
}

.mk2-confirm-cost-row.mk2-confirm-cost-sub{font-size:var(--text-meta);color:var(--text-tertiary);}

.mk2-confirm-cost-row.mk2-confirm-cost-balance{
  margin-top:4px;padding-top:8px;border-top:1px dashed var(--border-subtle, var(--border));
  font-size:var(--text-sm);color:var(--text-primary);font-weight:600;
}

.mk2-confirm-coin{color:#d97706;}

.mk2-confirm-warn{
  display:flex;align-items:flex-start;gap:var(--space-2);
  padding:10px 12px;border-radius:var(--radius-sm);
  background:var(--warning-soft);border-right:3px solid var(--warning);
  font-size:var(--text-meta);color:var(--text-primary);line-height:1.6;
}

.mk2-confirm-warn.is-danger{
  background:var(--danger-soft);border-right-color:var(--danger);color:var(--danger);
}

.mk2-confirm-warn strong{font-weight:900;}

/* R4-J: .mk2-confirm-actions / .mk2-confirm-btn-* / .mk2-confirm-hint are gone
   with the hand-rolled action row they dressed. The confirm modal's buttons are
   the central rm-footer's now (destructive at the start, secondary + primary at
   the end) so they wear the app's own .btn styles — a modal restating what
   every other modal already says is how five action rows drift apart. */

/* .finance-shell / .finance-scroll / .finance-content / .finance-tabs-wrap
   REMOVED (W-REVEAL, owner 2026-07-17 - one central entrance for every
   screen, the same staggered reveal billing runs).

   This bespoke shell was a pre-ScreenTabBar leftover that only finance +
   services ever used, and billing / inventory / analytics prove a TABBED
   screen needs none of it: `.screen-tabbar` owns `position: sticky` itself
   (see its own comment - "replaces every per-screen position:sticky patch")
   and the route scroller is `.main-content`. Keeping it cost twice:
     1. it padded HORIZONTALLY (14px) on top of --screen-inset, so its two
        screens sat 14px deeper than the other 13 (W-HDR round 2);
     2. it split the header into its own .reveal-stagger, and the cascade
        (.reveal-stagger > *:nth-child) counts DIRECT children per container -
        so the header's and the content's cascades each restarted at 0.04 and
        landed together instead of one after another.
   Both screens now render the app's ONE shape: [ScreenTabBar] +
   `.page-content reveal-stagger` holding the header AND the sections as
   siblings. See finance-screen.jsx / services-screen.jsx. */

/* V-Finance/5 (ADR-057): rich per-tab empty state — icon + title + hint
   (mirrors REC_KANBAN_EMPTY + MK_BOARD_EMPTY visual pattern). */

.fin-empty{
  display:flex;flex-direction:column;align-items:center;gap:var(--space-1-5);
  text-align:center;padding:48px 16px;color:var(--text-tertiary);
  font-size:var(--text-meta);font-weight:700;
}

.fin-empty-icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:14px;
  background:var(--bg-subtle);color:var(--text-tertiary);margin-bottom:4px;
}

.fin-empty-title{font-size:var(--text-md);font-weight:900;color:var(--text-primary)}

.fin-empty-hint{font-size:var(--text-meta);font-weight:600;color:var(--text-tertiary);max-width:44ch;line-height:1.65}

.fin-empty.is-success .fin-empty-icon{background:var(--success-soft);color:var(--success)}

.fin-empty.is-accent  .fin-empty-icon{background:var(--accent-soft);color:var(--accent-text)}

.fin-empty.is-warning .fin-empty-icon{background:var(--warning-soft);color:var(--warning)}

.fin-empty.is-danger  .fin-empty-icon{background:var(--danger-soft);color:var(--danger)}

.fin-empty.is-muted   .fin-empty-icon{background:var(--bg-subtle);color:var(--text-tertiary)}

/* .mk2-kpi strip removed (KpiCard unification Phase 1, 2026-07-09): dead family, 0 JSX consumers. Use MetricTile. */

/* ---------- workspace calendar card (the mk2-tl timeline zone it
   belonged to died in the R2 W3 purge) ---------- */

.mk2-card{
  background:var(--bg-card);border:1px solid var(--border);
  border-radius:var(--radius-lg);padding:var(--space-4);
}

/* LOADING / EMPTY retired (R3 Phase 3, 2026-07-10): .mk2-loading's last
   consumer was the MarketingWorkspace text loader (the feed GHOST is the
   loading skeleton now); the .mk2-empty family had no consumers since the
   ghost empty state replaced it. */

/* ---------- ZONE 4 : DETAIL CONTENT (drawer shell retired) ----------
   ADR-139 Wave 9 (2026-05-28): the bespoke .mk2-drawer-* shell rules
   are retired. All 3 marketing drawers (brand / settings / post) now
   render through ResponsiveModal so they inherit the premium shell +
   drag handle bottom-sheet + Ctrl+K animation curve. The remaining
   .mk2-detail-* / .mk-image-empty / .mk-reason rules below are used
   INSIDE the post-drawer body and stay. */

.mk2-detail-meta{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:var(--space-2);margin-bottom:14px;
}

.mk2-meta-cell{
  background:var(--bg-subtle);border-radius:var(--radius-sm);padding:8px 10px;
}

.mk2-meta-l{font-size:var(--text-micro);color:var(--text-tertiary);font-weight:700;margin-bottom:3px}

.mk2-meta-v{font-size:var(--text-meta);color:var(--text-primary);font-weight:800}

.mk2-detail-image{
  border:1px solid var(--border);border-radius:var(--radius);
  overflow:hidden;background:var(--bg-subtle);
}

.mk2-detail-image img{display:block;width:100%;aspect-ratio:1/1;object-fit:cover}

.mk2-detail-text{
  min-height:200px;padding:14px;
  border:1px solid var(--border);border-radius:var(--radius);
  background:var(--bg-subtle);white-space:pre-wrap;line-height:1.85;
  color:var(--text-primary);font-size:var(--text-sm);
}

/* ---------- RESPONSIVE ---------- */

@media (max-width:1180px){
}

@media (max-width:780px){
  /* ADR-139 W9: .mk2-drawer rule retired — drawers migrated to ResponsiveModal. */
}

/* ============================================================
   End Marketing Screen ? Concept V2 stylesheet
   ============================================================ */

/* ════════════════════════════════════════════════════════════════════
   V-PatientFile (ADR-055) — sticky header + full canvas + premium anim
   Replaces the pre-V layout (pf3-rail + pf3-canvas-with-command-and-
   clinical + pf3-drawer 3-col) where identity / next-appt / balance /
   tabs were duplicated 2-3× across 4 regions and the actual tab body
   got only ~25% of the grid.
   ════════════════════════════════════════════════════════════════════ */

/* Central clinical-alert strip shared by Patient File and workflow cards. */

.pf-hero-alerts{
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent);
}

.pf-hero-alerts__label{
  color: var(--text-tertiary);
  font-size: var(--text-meta);
  font-weight: 900;
  letter-spacing: 0;
  margin-inline-end: 4px;
}

.pf-alert{
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  font-size: var(--text-meta);
  font-weight: 800;
  white-space: nowrap;
  border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
  background: color-mix(in srgb, currentColor 14%, var(--bg-elevated));
  transition:
    transform var(--duration-fast) var(--ease-spring),
    box-shadow var(--duration-fast) var(--ease-smooth);
}

.pf-alert:hover{
  transform: translateY(var(--lift-1)) scale(1.03);
  box-shadow: var(--shadow-sm);
}

.pf-alert--danger{
  color: var(--danger);
  animation: pfAlertPulse 2.4s ease-in-out infinite;
}

.pf-alert--warning{ color: color-mix(in srgb, var(--warning) 80%, #000); }

.pf-alert--info{ color: var(--accent); }

.pf-alert--safe{ color: var(--success); }

/* Compact form of the same central clinical-alert strip for workflow cards. */

.medical-alerts-strip.is-compact{
  margin: 0;
  padding: 7px 9px;
  gap: 5px;
  border: 1px solid color-mix(in srgb, var(--success) 18%, var(--border));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--success) 5%, var(--bg-elevated));
}

.medical-alerts-strip.is-compact.has-critical{
  border-color: color-mix(in srgb, var(--danger) 28%, var(--border));
  background: color-mix(in srgb, var(--danger) 6%, var(--bg-elevated));
}

.medical-alerts-strip.is-compact .pf-hero-alerts__label{
  margin: 0;
  font-size: var(--text-micro);
}

.medical-alerts-strip.is-compact .pf-alert{
  padding: 3px 7px;
  gap: var(--space-1);
  font-size: var(--text-micro);
}

.medical-alerts-strip.is-compact .pf-alert--danger{ animation: none; }

@keyframes pfAlertPulse{
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 32%, transparent); }
  50%      { box-shadow: 0 0 0 6px color-mix(in srgb, var(--danger) 0%, transparent); }
}

/* Sticky tab strip — hosts the <Tabs> primitive */

/* ═══════════════════════════════════════════════════════════════════════
   Unified screen tab bar — ONE architecture for EVERY tabbed screen.
   Rendered by the shared ScreenTabBar component (app/src/ui/screen-tab-bar.jsx)
   as the first child under the topbar on reception / finance / services /
   inventory / analytics / patient-file / patients / …
     • position:sticky; top:0 pins it under the topbar on the single
       `.main-content` scroller (the app's one scroll model) — identical on
       every screen, so there is no per-screen sticky patch anymore.
     • background:var(--bg) = the page bg, so AT REST the bar is invisible
       (resting look unchanged) yet fully OPAQUE, so scrolled content can never
       bleed through it.
     • lib/screen-tab-scroll.js toggles `.is-scrolled` on `.main-content` while
       scrolled; the bar then lifts a hairline + soft shadow (the premium
       "stuck" state). No per-screen JS, no translucency. Replaces the old
       .stab-wrap elevation walker + every per-screen position:sticky patch. */

.screen-tabbar{
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 16px;
  margin-bottom: 12px;
  background: var(--bg);
  border-bottom: 1px solid transparent;
  transition: box-shadow 180ms ease, border-color 180ms ease,
              transform 0.22s ease, opacity 0.22s ease;
}

.screen-tabbar > .tabs{
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

/* W-GAP (owner 2026-07-17): the screen header used to float 36px under the tab
   bar — 8 (tabbar bottom padding) + 12 (tabbar margin) + 16 (the wrapper's top
   padding). That void was invisible while the header greeted ("صباح الخير …"),
   but W-HERO made the header announce the ACTIVE TAB, so the same word ended up
   printed twice with a gap between — it read as broken, not as breathing room.
   Tighten the chrome to ~16px so the bar + header land as ONE block.
   Three wrapper shapes exist, hence three selectors (each screen's own scroller):
     .page-content    → billing · patients · inventory · analytics
     .finance-scroll  → finance (its own flex scroller)
     + div > .page-content → reception (wraps its tab content in a scroll div)
   Settings is untouched on purpose: its tab strip is mobile-only and lives
   inside .settings-tabs-mobile, so it never matches — and on desktop its
   header legitimately keeps the full top padding under the topbar. */

.screen-tabbar{ margin-bottom: 4px; }

.screen-tabbar + .page-content,
.screen-tabbar + .finance-scroll,
.screen-tabbar + div > .page-content{ padding-top: 4px; }

/* W-AURORA: TRIED AND REJECTED — do not rebuild it (owner, 2026-07-17).
   Three variants were shipped to localhost and judged live:
     v1  light inside each ScreenHeader → also broke tab-swipe on reception +
         finance (a negative inset enlarged their scrollers' overflow, so
         lib/tab-swipe.js yielded the finger — see the note there);
     v2  ONE page-canvas layer, top band → "حلو" but the light died at an
         invisible line and left the rest of the page dead white;
     v2.1 the same layer lit edge to edge → heavy and murky: the tint sat under
         every white card and the page lost its crispness.
   Verdict: the app's surface stays FLAT. The premium reading comes from the
   identity chip (W-ICON), the tightened chrome above, and the content itself —
   not from ambient tint. The tab bar keeps its opaque background for the reason
   it always had one: nothing may bleed through it while content scrolls under. */

.screen-tabbar > .tabs::-webkit-scrollbar{ display: none; }

.screen-tabbar__aside{ flex-shrink: 0; display: flex; align-items: center; }

.main-content.is-scrolled .screen-tabbar{
  border-bottom-color: var(--border);
  box-shadow: 0 8px 18px -14px rgba(15, 23, 42, 0.5);
}

/* F3 (2026-07-09, owner): the tab bar hides on scroll-DOWN together with the
   topbar (same .chrome-hidden signal from lib/screen-tab-scroll.js) and slides
   back on scroll-up / near the top, so both chrome layers clear for content. */

.main.chrome-hidden .screen-tabbar{
  transform: translateY(-100%);
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce){
  .screen-tabbar{ transition: box-shadow 180ms ease, border-color 180ms ease; }
}

/* Mobile: stack the tabs over the aside (e.g. Finance's date pill) so all tabs
   stay reachable, matching the old finance-tabs-wrap behaviour. */

/* W-HDR (owner 2026-07-17): was 720px - the third breakpoint in the same
   vertical band (header 640 / tabs 720 / topbar+KPI+bottom-nav 768), so
   the three chrome layers a phone stacks flipped at three different
   widths. Joined to the app's ONE device line (768 = --bp-tablet). */

@media (max-width: 768px){
  .screen-tabbar{
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
    padding: 8px 12px;
  }
  .screen-tabbar > .tabs{ width: 100%; }
  .screen-tabbar__aside{ width: 100%; }
  .screen-tabbar__aside > *{ width: 100%; }
}

/* Responsive */

@media (max-width: 980px){
  .pf-hero-alerts{ gap: var(--space-1-5); }
}

/* Honour prefers-reduced-motion — kill the candy, keep the layout */

@media (prefers-reduced-motion: reduce){
  .pf-alert--danger{ animation: none; }
  .pf-alert{ transition: none; }
}

/* ── Pre-V-PatientFile (pf3-*) layout — REMOVED. Replaced by .pf-* above.
   The full pre-V audit + removal rationale is in DECISIONS.md ADR-055. ── */

/* (pf3-* layout block deleted in V-PatientFile / ADR-055 — see .pf-* CSS above) */

/* ════════════════════════════════════════════════════════════════════
   ✨ PREMIUM UX/UI POLISH — Phase 1 (additive, safe to revert)
   ════════════════════════════════════════════════════════════════════ */

/* ── Design tokens: spacing + motion ─────────────────────────────── */

:root {
  /* ADR-291 W2: the grid is 4px, with TWO documented half-steps. 6px and 10px
     are used 169 times between them — a dense clinical UI genuinely needs a
     rung between 4 and 8 and between 8 and 12 for icon gaps and chip padding.
     Naming them makes that legitimate and, more importantly, closes the door on
     the 1 / 2 / 3 / 5 / 7 / 9px one-offs beside them, which have no rationale.
     HALF-STEPS ARE FOR TIGHT SPACING ONLY (under 12px). Anything larger stays
     on the whole grid. */
  --space-1:  4px;
  --space-1-5: 6px;
  --space-2:  8px;
  --space-2-5: 10px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;

  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);

  --duration-fast: 120ms;
  --duration-base: 200ms;
  --duration-slow: 320ms;

  /* ── ADR-290: ONE entrance for every modal-family surface ────────────
     Owner 2026-07-25: "أي ريسبونسف مودال يفتح بالراحة بطريقة بريميم — بحس
     إنه بيفتح بسرعة". Measured first: the app had SIX different opens
     (150 / 220 / 240 / 280 / 320 / 320ms across four curves), which is why
     some modals read snappier than others.
     The complaint was not really about DURATION. `--ease-out`
     (0.16, 1, 0.3, 1) is a violent ease-out: it covers 83% of the distance
     in the first QUARTER of the time, so a 280ms modal is perceptually a
     ~92ms one. Pair that with 18px of travel and the eye has nothing to
     follow. So: a curve that spends its time moving, more distance to move
     through, and the scrim dimming FIRST so the card lands on an already-
     settled backdrop — the part that reads expensive. Perceived open goes
     92ms -> 213ms while the nominal number only moves 280 -> 340.
     Every modal points here, so they can never drift apart again. */
  --modal-in: 340ms;
  --modal-in-ease: cubic-bezier(0.34, 1.16, 0.64, 1);
  --modal-lead: 60ms;
}

/* ── Smooth, premium scrollbars (everywhere) ─────────────────────── */

* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}

*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
  transition: background var(--duration-fast) var(--ease-smooth);
}

*::-webkit-scrollbar-thumb:hover {
  background: var(--text-tertiary);
  background-clip: padding-box;
}

/* ── Card hover lift (premium feel on interactive cards) ─────────── */

.card {
  transition:
    box-shadow var(--duration-base) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-base) var(--ease-smooth),
    background-color var(--duration-fast) var(--ease-smooth);
}

@media (hover: hover) {
.card.interactive:hover,
.card[role="button"]:hover {
  transform: translateY(var(--lift-2));
  box-shadow: var(--shadow-lg);
  cursor: pointer;
}
}

.card.interactive:active,
.card[role="button"]:active {
  transform: translateY(0);
  transition-duration: 60ms;
}

/* ── Button micro-interactions ───────────────────────────────────── */

.btn {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-fast) var(--ease-smooth);
}

@media (hover: hover) {
.btn:hover:not(:disabled) {
  transform: translateY(var(--lift-1));
}
}

.btn:active:not(:disabled) {
  transform: translateY(0);
  transition-duration: 60ms;
}

@media (hover: hover) {
.btn.primary:hover:not(:disabled) {
  box-shadow: 0 6px 16px color-mix(in srgb, var(--accent) 28%, transparent);
}
}

/* ── U-10/3: Loading state for async buttons ──────────────────────
   Trigger:  add aria-busy="true" alongside disabled:<state> at the
             call site (mirrors data-table.jsx's aria-busy idiom).
   Visual:   currentColor spinner via ::before, caller's icon child
             hidden, full color preserved (overrides the .55 dim
             from the generic .btn:disabled rule so the loading
             button stays clearly the same primary/danger/etc).
   Animates: reuses the existing @keyframes spin from L1653 — no
             new keyframes block. */

.btn[aria-busy="true"] {
  cursor: progress;
}

.btn[aria-busy="true"]:disabled {
  opacity: 1;
  cursor: progress;
}

.btn[aria-busy="true"] > svg {
  display: none;
}

.btn[aria-busy="true"]::before {
  content: '';
  display: inline-block;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

.btn.sm[aria-busy="true"]::before {
  width: 12px;
  height: 12px;
  border-width: 1.5px;
}

@media (prefers-reduced-motion: reduce) {
  .btn[aria-busy="true"]::before { animation: none; }
}

/* ── Unified focus rings (accessibility + premium) ─────────────────
   U-10/2: uses the canonical --ring-shadow / --ring-shadow-soft
   tokens from U-1 (tokens.css) instead of inline color-mix. Adds
   .icon-btn + .nav-item to the selector list — they were previously
   covered by a legacy `outline: 2px solid` rule that's now removed
   to eliminate the double-ring inconsistency on those primitives. */

button:focus-visible,
a:focus-visible,
.tab:focus-visible,
.btn:focus-visible,
.icon-btn:focus-visible,
.nav-item:focus-visible,
[role="button"]:focus-visible {
  outline: none;
  box-shadow: var(--ring-shadow);
  border-radius: var(--radius);
}

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring-shadow-soft);
}

/* ── Interaction States System (Chrome Wave E, 2026-07-08, owner) ──────────
   ONE central vocabulary for hover/focus so surfaces stop hand-rolling their
   own values (the accent-border hover was sidebar-only; now it is a reusable
   recipe). Six recipes by ROLE — centralize what must be consistent,
   specialize what must be meaningful (the golden rule) — all driven by the
   tokens below so a theme change flows everywhere and the FEEL is tuned in ONE
   place:
     1. .hover-surface  — subtle bg lift (menus, plain rows, icon buttons)
     2. .hover-emphasis — bg + accent border (nav / important actions; the
                          treatment the sidebar shipped, now reusable anywhere)
     3. .hover-row      — very subtle full-row tint (dense tables)
     4. .hover-lift     — shadow + slight rise (cards / tiles)
     5. button hovers   — owned by the .btn variant system (already central)
     6. focus-ring      — the --ring-shadow :focus-visible block above (a11y)
   Adopt incrementally: add the recipe class (or point a rule at --state-*),
   never a new bespoke hover value. */

:root {
  --state-duration: var(--duration-fast, 120ms);
  --state-ease: var(--ease-out, cubic-bezier(0.16, 1, 0.3, 1));
  --state-hover-surface: var(--bg-hover);
  --state-emphasis-border: var(--accent);
  --state-row-hover: var(--bg-hover);
  --state-lift-y: var(--lift-1, -2px);
  --state-lift-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.18);
}

.hover-surface { transition: background var(--state-duration) var(--state-ease); }

.hover-emphasis {
  border: 1px solid transparent;
  transition: background var(--state-duration) var(--state-ease),
              border-color var(--state-duration) var(--state-ease);
}

.hover-row { transition: background var(--state-duration) var(--state-ease); }

.hover-lift {
  transition: transform var(--state-duration) var(--state-ease),
              box-shadow var(--state-duration) var(--state-ease);
}

/* Hover Wave 3b: the four recipe hovers live under ONE hover-capability
   guard (bases above stay unguarded - they only declare transitions). */

@media (hover: hover) {
.hover-surface:hover { background: var(--state-hover-surface); }
.hover-emphasis:hover {
  background: var(--state-hover-surface);
  border-color: var(--state-emphasis-border);
}
.hover-row:hover { background: var(--state-row-hover); }
.hover-lift:hover {
  transform: translateY(var(--state-lift-y));
  box-shadow: var(--state-lift-shadow);
}
}

@media (prefers-reduced-motion: reduce) {
  .hover-lift:hover { transform: none; }
}

/* ── Tab active indicator: smooth slide ──────────────────────────── */

.tabs .tab {
  position: relative;
  transition: color var(--duration-fast) var(--ease-smooth);
}

/* Hover Wave 2a: the tab hover color that lived here merged into the ONE
   .tabs .tab:hover:not(.active) rule in the tabs base section. */

/* ── Premium animations library ──────────────────────────────────── */

@keyframes slideUp {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes slideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes slideInStart {
  from { opacity: 0; transform: translateX(-12px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

@keyframes pulseSoft {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.65; }
}

.slide-up   { animation: slideUp   var(--duration-slow) var(--ease-out); }

.slide-down { animation: slideDown var(--duration-base) var(--ease-out); }

.slide-in   { animation: slideInStart var(--duration-base) var(--ease-out); }

.pulse-soft { animation: pulseSoft 2s ease-in-out infinite; }

/* ── KPI numeric polish (tabular figures align nicely) ───────────── */

[data-numeric] {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.025em;
}

/* ── Grid + flex tighter alignment helpers (additive) ───────────── */

.stack { display: flex; flex-direction: column; }

.stack.gap-4 { gap: var(--space-4); }

.row { display: flex; align-items: center; }

.row.gap-4 { gap: var(--space-4); }

/* ── RTL/LTR direction support via logical properties ───────────── */

html[dir="ltr"],
[dir="ltr"] {
  text-align: left;
}

[dir="ltr"] body,
[dir="ltr"] button,
[dir="ltr"] input,
[dir="ltr"] select,
[dir="ltr"] textarea {
  direction: ltr;
  text-align: left;
}

/* ── Selection color (matches accent) ────────────────────────────── */

::selection {
  background: color-mix(in srgb, var(--accent) 25%, transparent);
  color: var(--text-primary);
}

/* ── Image rendering polish ──────────────────────────────────────── */

img {
  -webkit-user-drag: none;
  user-select: none;
}

/* (.data-table tbody tr transition consolidated into the ONE Tables shell
   above — 2026-07-12.) */

/* ── Inputs feel premium ─────────────────────────────────────────── */

.input,
.select,
.textarea {
  transition:
    border-color var(--duration-fast) var(--ease-smooth),
    box-shadow   var(--duration-fast) var(--ease-smooth),
    background   var(--duration-fast) var(--ease-smooth);
}

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

/* ── Soft entrance for screen content ────────────────────────────── */

main [data-screen],
main > section {
  animation: slideUp var(--duration-slow) var(--ease-out);
}

/* ── Respect users who prefer reduced motion ─────────────────────── */

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

/* ── Mobile spacing tweaks ───────────────────────────────────────── */

@media (max-width: 720px) {
  .card { border-radius: var(--radius); }
  .btn { padding: 9px 14px; }
}

/* ════════════════════════════════════════════════════════════════════
   ✨ PREMIUM UX/UI POLISH — Phase 2 (Sidebar + Topbar)
   ════════════════════════════════════════════════════════════════════ */

/* ── Sidebar: subtle depth gradient ──────────────────────────────── */

.sidebar {
  position: relative;
  background-image:
    linear-gradient(180deg,
      var(--bg-sidebar) 0%,
      color-mix(in srgb, var(--bg-sidebar) 92%, var(--bg-subtle)) 100%);
}

/* ── Nav items: active indicator stripe + slide on hover ─────────── */

.nav-item {
  position: relative;
  overflow: visible;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color      var(--duration-fast) var(--ease-smooth),
    transform  var(--duration-base) var(--ease-smooth);
}

/* Active accent stripe — animated in via scaleY */

.nav-item::before {
  content: '';
  position: absolute;
  inset-inline-start: -10px;
  top: 8px;
  bottom: 8px;
  width: 3px;
  background: var(--accent);
  border-radius: 0 3px 3px 0;
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--duration-base) var(--ease-spring);
  pointer-events: none;
}

.nav-item.active::before {
  transform: scaleY(1);
}

/* Subtle slide on hover (RTL-aware) */

@media (hover: hover) {
.nav-item:hover:not(.active) {
  transform: translateX(-2px);
}
[dir="ltr"] .nav-item:hover:not(.active) {
  transform: translateX(2px);
}
}

/* Icon-box gets accent tint + soft scale when active */

.nav-item .icon-box {
  border-radius: var(--radius-md);
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color      var(--duration-fast) var(--ease-smooth),
    transform  var(--duration-base) var(--ease-spring);
}

/* W-ICON: the ACTIVE rail row wears the identity glass (scaled to 20px, no
   drop shadow at this size). Rest rows stay bare glyphs on purpose — chipping
   all 12 would out-shout the content and erase the meaning of "active". */

.nav-item.active .icon-box {
  border-radius: 7px;
  color: var(--accent);
  background:
    linear-gradient(145deg,
      color-mix(in srgb, var(--accent) 26%, var(--bg-elevated)),
      color-mix(in srgb, var(--accent) 8%, var(--bg-elevated)));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);
  transform: scale(1.06);
}

.nav-item:hover:not(.active) .icon-box {
  color: var(--text-primary);
}

/* ── Avatar: premium polish ──────────────────────────────────────── */

.avatar {
  position: relative;
  overflow: hidden;
  box-shadow:
    0 4px 12px color-mix(in srgb, var(--accent) 22%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
  transition: transform var(--duration-base) var(--ease-spring);
}

.avatar::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255,255,255,0.18), transparent 55%);
  pointer-events: none;
}

.clinic-switcher-trigger:hover .clinic-switcher-avatar {
  transform: scale(1.05);
}

/* ── Sidebar footer logout button: unified accent hover (2026-07-08,
   owner "مركزية وتناسق") - same accent-border highlight as the nav items
   + the demo toggle. SINGLE source for the logout hover; the old
   danger-red hover was retired so the rail's mouseover is consistent. */

.sidebar-footer .icon-btn {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color      var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth);
}

@media (hover: hover) {
.sidebar-footer .icon-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--accent);
}
}

/* ── Logo: gentle scale on hover ─────────────────────────────────── */

/* ── Topbar: glassy premium look ─────────────────────────────────── */

/* THIS block is the single owner of the topbar's position + layering (it
   also carries the backdrop-filter that creates the stacking context).
   z-index 90 (owner bug 2026-07-10, round 2): at 50 it TIED the sticky
   .screen-tabbar (z:50) and the tabs, painting later in the DOM, covered
   every topbar dropdown (theme/lang/announcements/switcher). 90 = above
   the content chrome (tabs 50, pf-tab-strip 80), below modals (1000+).
   The earlier duplicate z-index on the base .topbar block was REMOVED -
   two competing declarations were exactly how this regressed. */

.topbar {
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
  position: sticky;
  top: 0;
  z-index: 90;
}

/* ── Topbar icon buttons polished ────────────────────────────────── */

.icon-btn {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color      var(--duration-fast) var(--ease-smooth),
    transform  var(--duration-fast) var(--ease-smooth);
}

@media (hover: hover) {
.icon-btn:hover {
  /* Wave E: pulls from the interaction-states token (== --bg-hover today) so
     the topbar/icon hover feel is tuned centrally, not per-rule. */
  background: var(--state-hover-surface);
  color: var(--text-primary);
  transform: scale(1.06);
}
}

.icon-btn:active {
  transform: scale(0.96);
}

/* ── Page title polish ───────────────────────────────────────────── */

.page-title {
  letter-spacing: -0.015em;
}

/* ── Sidebar nav: stagger entry animation (subtle) ──────────────── */

.sidebar .nav-item {
  animation: slideInStart var(--duration-base) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 25ms);
}

.sidebar .nav-items:nth-of-type(1) .nav-item:nth-child(1) { --i: 1; }

.sidebar .nav-items:nth-of-type(1) .nav-item:nth-child(2) { --i: 2; }

.sidebar .nav-items:nth-of-type(1) .nav-item:nth-child(3) { --i: 3; }

.sidebar .nav-items:nth-of-type(2) .nav-item:nth-child(1) { --i: 4; }

.sidebar .nav-items:nth-of-type(2) .nav-item:nth-child(2) { --i: 5; }

.sidebar .nav-items:nth-of-type(3) .nav-item:nth-child(1) { --i: 6; }

.sidebar .nav-items:nth-of-type(3) .nav-item:nth-child(2) { --i: 7; }

.sidebar .nav-items:nth-of-type(4) .nav-item:nth-child(1) { --i: 8; }

/* ════════════════════════════════════════════════════════════════════
   ✨ PREMIUM UX/UI POLISH — Phase 3 (Dashboard + Reception)
   ════════════════════════════════════════════════════════════════════ */

/* ── Dashboard KPI stagger RETIRED (W-REVEAL 2026-07-17) ──────────────
   The .reveal-stagger section cascade (reveal-utils.js) is the ONE entrance.
   This dashboard-era .grid.cols-4 ramp ran in PARALLEL with it - a second
   nth-child stagger on the KPI row. KPI numbers now enter via MetricTile
   CountUp and the row floats in with its .reveal-stagger section. */

/* ── Page header polish ──────────────────────────────────────────── */

.page-header {
  animation: slideDown var(--duration-slow) var(--ease-out);
}

/* ── Per-card auto-entry RETIRED (W-REVEAL 2026-07-17) ────────────────
   A card floats in WITH its section under .reveal-stagger; there is no
   independent per-card entrance (that was the second half of the double
   cascade the owner saw). The reveal is the single entrance. */

/* ── Section headers: better typographic hierarchy ───────────────── */

.card > div:first-child[style*="text-transform"] {
  letter-spacing: 0.05em;
}

/* ── Kanban board: premium polish ────────────────────────────────── */

.kanban-board {
  scroll-snap-type: x proximity;
  gap: var(--space-3);
}

.kanban-col {
  position: relative;
  transition:
    border-color var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-base) var(--ease-smooth);
  scroll-snap-align: start;
  /* entrance RETIRED (W-REVEAL 2026-07-17): the reception board floats in with
     its .reveal-stagger section - the columns no longer self-stagger in a
     parallel ramp. One entrance per screen. */
}

.kanban-col:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-sm);
}

/* Top accent stripe via header bg-elevated tint */

.kanban-col-hdr {
  position: relative;
  letter-spacing: 0.02em;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg,
    var(--bg-elevated) 0%,
    color-mix(in srgb, var(--bg-elevated) 92%, var(--bg-subtle)) 100%);
}

.kanban-col-hdr::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: currentColor;
  opacity: 0.4;
}

/* Kanban cards premium hover */

.kanban-card {
  transition:
    box-shadow var(--duration-base) var(--ease-smooth),
    transform var(--duration-base) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth);
  will-change: transform;
}

/* Hover Wave 2b: hover consolidated into the ONE rule in the kanban
   redesign section (its border tint moved there). The base transition
   above stays - it animates all three hover properties. */

/* Empty state subtle pulse */

.kanban-empty {
  animation: pulseSoft 3s ease-in-out infinite;
}

/* ── Chip enhancements (status badges) ──────────────────────────── */

.chip {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

@media (hover: hover) {
.chip:hover {
  transform: scale(1.04);
}
}

.chip:active {
  transform: scale(0.98);
  transition-duration: 60ms;
}

/* ── Avatar small variant: premium gradient feel ──────────────── */

.avatar.sm {
  position: relative;
  overflow: hidden;
  box-shadow:
    0 2px 6px rgba(0, 0, 0, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.15);
}

.avatar.sm::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255,255,255,0.12), transparent 55%);
  pointer-events: none;
  border-radius: inherit;
}

/* ── Mini bar chart bars: subtle entry ───────────────────────────── */

.card .grid + div > div > div[style*="height:"],
[data-chart-bar] {
  transition: opacity var(--duration-base) var(--ease-out);
}

/* ── Progress bar premium ────────────────────────────────────────── */

.progress {
  background: var(--bg-subtle);
  border-radius: var(--radius-pill);
  overflow: hidden;
  height: 6px;
  position: relative;
}

.progress::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    transparent,
    rgba(255, 255, 255, 0.18),
    transparent);
  animation: shimmer 2.5s ease-in-out infinite;
  pointer-events: none;
}

.progress-bar {
  height: 100%;
  border-radius: var(--radius-pill);
  transition: width var(--duration-slow) var(--ease-out);
}

/* ── Walk-In modal patient list polish ───────────────────────────── */

.modal-backdrop .gm-box .input:focus + div,
.gm-box [style*="overflowY"] > div {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

/* ── Selectable option-card pattern (used in modals) ─────────────── */

button.card[style*="border"] {
  transition:
    border-color var(--duration-fast) var(--ease-smooth),
    background var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-fast) var(--ease-smooth);
}

button.card[style*="border"]:hover {
  transform: translateY(var(--lift-1));
}

/* ── Reception kanban: mobile responsive ──────────────────────────
   RETIRED 2026-07-05 (Nav-Kanban consolidation). The old ≤900px
   5-column horizontal-scroll layout was fully overridden by the
   adaptive `.kanban-board.kanban-full-width` tiers (chips / 2-col /
   3-col) below and only added cascade fragility. Single source of
   truth for the kanban responsive model now lives in the
   "V-Phase2 Wave 2 — Adaptive kanban" block. */

/* ── Wave 5e fix: EditInvoiceModal 2-panel body (2:1 desktop, stack phone) ──
   The original inline `2fr 1fr` grid kept the items table panel ~2x
   wider than the summary panel on desktop so the 5-col table (service
   select + qty + price + total + delete) had room to breathe. The
   first Wave 5e attempt swapped it for .l-grid--fixed-2 (always 50/50)
   which left the items table cramped on desktop. This class restores
   the 2:1 ratio on tablet+ and stacks to 1-col on phones via the
   720px breakpoint, matching the rest of the modal-body responsive
   pattern. */

/* V-Phase2 Wave 4b (ADR-134): `.edit-invoice-2col` retired.
   EditInvoiceModal now uses the unified vertical card-stack
   pattern (no 2fr+1fr split), so this class has no JSX
   consumers and the desktop 2:1 ratio is no longer needed.
   The new layout matches AddSessionModal + LabOrderModal +
   EditPurchaseInvoiceModal: header row → items cards →
   summary card → notes textarea. */

/* ── Wave 5c: Appointments WeekView 7-col stack on phones (<=480px) ──
   The original repeat(7, 1fr) lets 7 day columns share the viewport,
   which works on tablet+ but on a 360px phone gives each day cell
   ~51px - not enough room for the appointment pills (fontSize 10,
   patient name + time) to be readable. Stacking to 1 col makes each
   day a full sheet-width card; appointment lists inside stay readable.
   The 7-col fallback stays for the 481-768px tablet range.

   Override carries the inline `grid-template-columns` set in
   appointments.jsx (the WeekView grid is inline-styled), so the rule
   needs the same specificity (class + style attr selector). The
   element also carries `display: grid` inline, so we only need to
   change `grid-template-columns`. */

/* ── Wave 5c: Marketing calendar mk-cal-grid stack on phones (<=480px) ──
   Same pattern as the appointments week grid above. The marketing
   calendar is a 7-col x 4-row month view of scheduled posts; on
   <=720px we already shrink the font, but the 7 columns at 360px
   give each post card too little width. Stack the 28 cells vertically
   so each scheduled-post pill gets full width. */

@media (max-width: 480px) {
}

/* ── Wave 5b: Reception kanban vertical stack (<=480px) ──
   RETIRED 2026-07-05 (Nav-Kanban consolidation). The single-column
   vertical stack was superseded by the adaptive chip view (one stage
   at a time) which now covers every phone up to 600px. See the
   "V-Phase2 Wave 2 — Adaptive kanban" block for the live model. */

/* ════════════════════════════════════════════════════════════════════
   V-Reception (Phase 5 Wave 2) — chip-tone count + per-stage empty +
   terminal-state softening. Touches:
     • .kanban-count          → tighter chip sizing for the column tally
     • .kanban-empty-rich     → 3-line per-stage empty state body
     • .kanban-col-hdr        → +2px vertical padding (better hit area)
     • .kanban-col[data-stage="done"|"cancelled"] → terminal opacity
   The legacy `.reception-insight-strip` wrapper for the surfaced
   "نصيحة Modirify" Dashboard InsightCard was retired on 2026-06-02
   (owner direction: advice strip read as noise on the busiest screen).
   `.reception-hero-clock` (ReceptionHero's live clock pill) was retired
   2026-07-06 with ReceptionHero itself (Unify W6/W7 topbar-is-header).
   ──────────────────────────────────────────────────────────────────── */

/* Tighten the chip when it lives in the kanban column header — the
   default chip padding feels chunky at this scale. tabular-nums keeps
   the digit width stable as cards.length crosses single → double. */

.kanban-col-hdr .chip.kanban-count {
  padding: 1px 8px;
  font-size: var(--text-xs);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}

/* Slightly taller header — old 8px 10px felt cramped vs the new chip. */

.kanban-col-hdr {
  padding: 10px 12px;
}

/* Terminal stages: done + cancelled are "settled" states, soften their
   header to imply finished work without losing readability. */

.kanban-col[data-stage="done"] .kanban-col-hdr,
.kanban-col[data-stage="no_show"] .kanban-col-hdr,
.kanban-col[data-stage="cancelled"] .kanban-col-hdr {
  opacity: 0.92;
}

/* Rich per-stage empty state — replaces the legacy "icon + لا يوجد
   مرضى" with a 3-line layout (icon → title → hint). The .kanban-empty
   wrapper still owns padding + dashed border + pulseSoft animation;
   .kanban-empty-rich just structures the inner content. */

.kanban-empty-rich {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
}

.kanban-empty-rich .kanban-empty-icon {
  font-size: var(--text-title);
  opacity: 0.55;
  line-height: 1;
}

.kanban-empty-rich .kanban-empty-title {
  font-size: var(--text-meta);
  font-weight: 700;
  color: var(--text-secondary);
}

.kanban-empty-rich .kanban-empty-hint {
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-align: center;
  max-width: 170px;
  line-height: 1.5;
}

/* When the column is a drop target, the parent .kanban-empty swaps to
   the "↓ اسحب هنا ↓" literal — kill the rich-empty animation noise so
   the drop affordance reads as a single beat. */

.kanban-empty.drop-active .kanban-empty-rich {
  display: none;
}

/* ════════════════════════════════════════════════════════════════════
   V-Appointments (Phase 5 Wave 2) — premium polish on Day/Week/Month
   + Reception-Dashboard merge surface (4 dashboard cards inline).
   ──────────────────────────────────────────────────────────────────── */

/* Today's calendar cell — kanban-style ring + accent-tinted bg so the
   "you are here" beacon is visible even on busy months. The existing
   .calendar-day.today rules in the original block carry the base
   styling; this override strengthens the visual beat. */

.calendar-day.today {
  background: color-mix(in srgb, var(--accent) 5%, var(--bg-elevated));
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* Reception quick-row — 4-card grid hosting the surfaced Dashboard
   cards (Doctors / LowStock / RecentPatients / Outstanding). Grid
   collapses to 2 cols on tablet, 1 col on mobile. */

.reception-quick-row {
  /* layout is inline-styled; CSS hook for future surface adjustments. */
}

@media (max-width: 768px) {
  .reception-quick-row {
    grid-template-columns: 1fr !important;
  }
}

/* Appointments controls row — view toggle + filter dropdown + export.
   Just a hook for future adjustments; the row is laid out via inline
   flex styles. */

.appt-controls {
  /* hook */
}

/* ── Dashboard: doctor status row & list-item hovers (CSS-driven) ─ */

.card [style*="cursor:"][style*="pointer"]:not(.btn):not(.icon-btn) {
  border-radius: var(--radius-sm);
}

/* ── Empty state visual ──────────────────────────────────────────── */

.empty-state {
  animation: slideUp var(--duration-slow) var(--ease-out);
}

/* ════════════════════════════════════════════════════════════════════
   ✨ PREMIUM UX/UI POLISH — Phase 4 (Patients + Patient File)
   ════════════════════════════════════════════════════════════════════ */

/* (.data-table premium-polish base — sticky header, hover, transitions —
   consolidated into the ONE Tables shell near the top of this file
   (2026-07-12). It used to re-set the header to 700/secondary/0.01em,
   overriding the base; now there is a single header rule. Only the
   clickable hover-stripe below stays here — it is functional + specific.) */

/* ── Clickable table rows: hover indicator stripe (opt-in) ───────── */

.data-table.clickable tbody tr,
.data-table tbody tr[onclick],
.data-table tbody tr[role="button"] {
  cursor: pointer;
}

/* ADR-055 amendment #3: hover stripe is hosted on td:first-child, not
   on the tr itself. A ::before pseudo-element on <tr> with
   position: absolute (and tr position: relative) gets counted as an
   anonymous cell by Chrome's table column-width algorithm in
   direction: rtl, which shifts every tbody td by one column relative
   to the thead — breaking column alignment in the Invoices +
   Sessions tables. Hosting the pseudo on the first td keeps the
   stripe visible at the row's start edge without polluting column
   counting. Same visual; correct layout. */

.data-table.clickable tbody tr > td:first-child,
.data-table tbody tr[onclick] > td:first-child,
.data-table tbody tr[role="button"] > td:first-child {
  position: relative;
}

.data-table.clickable tbody tr > td:first-child::before,
.data-table tbody tr[onclick] > td:first-child::before,
.data-table tbody tr[role="button"] > td:first-child::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--duration-base) var(--ease-spring);
  pointer-events: none;
}

.data-table.clickable tbody tr:hover > td:first-child::before,
.data-table tbody tr[onclick]:hover > td:first-child::before,
.data-table tbody tr[role="button"]:hover > td:first-child::before {
  transform: scaleY(0.7);
}

/* (pf3-* polish premium block deleted in V-PatientFile / ADR-055) */

/* ── Patients stat-tile auto-entry RETIRED (W-REVEAL 2026-07-17) ──────
   Same story as the other card auto-entrances: these tiles float in with
   their .reveal-stagger section, not on a parallel slideUp timeline. */

/* ── Search input premium polish (used in patients filter) ───────── */

.search-input,
input[type="search"] {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-fast) var(--ease-smooth);
}

input[type="search"]:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* ── Filter chips/pills (used on patients & lists) ───────────────── */

.filter-chip,
.pill {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth);
  cursor: pointer;
}

@media (hover: hover) {
.filter-chip:hover,
.pill:hover {
  transform: translateY(var(--lift-1));
  border-color: var(--border-strong);
}
}

.filter-chip.active,
.pill.active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-text);
}

/* ── Patient avatars in lists (consistent visual) ────────────────── */

.avatar {
  position: relative;
  transition: transform var(--duration-base) var(--ease-spring);
}

@media (hover: hover) {
tr:hover .avatar,
[role="row"]:hover .avatar {
  transform: scale(1.05);
}
}

/* (pf3-subscreen stagger + pf3-med-alert animation deleted in V-PatientFile / ADR-055) */

/* ── Modal polish for patient quick-add etc. ─────────────────────── */

.modal-backdrop {
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
}

/* ── Loading spinner premium ─────────────────────────────────────── */

[style*="animation: spin"] {
  filter: drop-shadow(0 2px 4px color-mix(in srgb, var(--accent) 30%, transparent));
}

/* ── Table cell numeric alignment ────────────────────────────────── */

.data-table td[data-numeric],
.data-table td.numeric {
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* ── Mobile patient file polish (safe overrides) ─────────────────── */

@media (max-width: 720px) {
  .data-table {
    font-size: var(--text-meta);
  }
}

/* ════════════════════════════════════════════════════════════════════
   ✨ PHASE 4.5 — UNIFIED CARD DESIGN SYSTEM
   ════════════════════════════════════════════════════════════════════
   5 variants. Use them everywhere. Replace inline JS hover handlers.

   .card                — base container, no hover
   .card.kpi            — stat card with hover lift + accent border
   .card.interactive    — clickable card, stronger hover lift + cursor
   .card.section        — content container, gentle hover on border only
   .card.feature        — hero/highlight card with accent stripe
   ──────────────────────────────────────────────────────────────────── */

/* ── .card.section: content container, subtle ───────────────────── */

.card.section {
  cursor: default;
}

.card.section:hover {
  border-color: var(--border-strong);
  /* no transform, no shadow change — keeps lists/tables stable */
}

/* ── .card.interactive: clickable, stronger hover ────────────────── */

.card.interactive {
  cursor: pointer;
  position: relative;
}

/* Hover Wave 2a: motion (lift + shadow + cursor) lives in the grouped
   .card.interactive/.card[role="button"] rule in the micro-interactions
   section; this block keeps ONLY its unique border accent so the same
   values are never declared twice. */

@media (hover: hover) {
.card.interactive:hover {
  border-color: color-mix(in srgb, var(--accent) 25%, var(--border));
}
}

/* ── .card.feature: hero with accent stripe ──────────────────────── */

.card.feature {
  position: relative;
  border-inline-start: 4px solid var(--accent);
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--accent) 4%, var(--bg-card)) 0%,
      var(--bg-card) 60%);
  transition:
    box-shadow var(--duration-base) var(--ease-smooth),
    transform var(--duration-base) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth);
}

.card.feature:hover {
  transform: translateY(var(--lift-2));
  box-shadow: var(--shadow);
}

/* Feature variants by tone */

.card.feature.success { border-inline-start-color: var(--success); }

.card.feature.success { background: linear-gradient(135deg, color-mix(in srgb, var(--success) 4%, var(--bg-card)) 0%, var(--bg-card) 60%); }

.card.feature.danger  { border-inline-start-color: var(--danger); }

.card.feature.danger  { background: linear-gradient(135deg, color-mix(in srgb, var(--danger) 4%, var(--bg-card)) 0%, var(--bg-card) 60%); }

.card.feature.warning { border-inline-start-color: var(--warning); }

.card.feature.warning { background: linear-gradient(135deg, color-mix(in srgb, var(--warning) 4%, var(--bg-card)) 0%, var(--bg-card) 60%); }

.card.feature.info    { border-inline-start-color: var(--info); }

.card.feature.info    { background: linear-gradient(135deg, color-mix(in srgb, var(--info) 4%, var(--bg-card)) 0%, var(--bg-card) 60%); }

.card.feature.purple  { border-inline-start-color: var(--purple); }

.card.feature.purple  { background: linear-gradient(135deg, color-mix(in srgb, var(--purple) 4%, var(--bg-card)) 0%, var(--bg-card) 60%); }

/* ── Universal grid auto-stagger RETIRED (W-REVEAL 2026-07-17) ────────
   This was the loudest duplicate of the reveal: a second *:nth-child ramp
   firing on EVERY .grid of .cards in parallel with the .reveal-stagger
   section cascade (15 of 18 revealed screens ran both at once - the owner's
   "everything loads together"). The reveal is the single entrance now; a
   nested grid enters with its section, not on its own timeline. */

/* ── Make sure hover lift never clips children visually ─────────── */

.card.interactive,
.card.feature {
  will-change: transform;
}

/* ════════════════════════════════════════════════════════════════════
   PHASE 5 -- Billing + Finance premium polish
   ════════════════════════════════════════════════════════════════════ */

/* -- Invoice/transaction rows: cursor + hover ---------------------- */

/* -- Status chips: subtle pulse for unpaid/overdue ----------------- */

.chip.danger,
.chip.warning {
  position: relative;
}

.chip.danger::after,
.chip.warning::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  background: currentColor;
  opacity: 0;
  z-index: -1;
  animation: chipGlow 2s ease-in-out infinite;
}

@keyframes chipGlow {
  0%, 100% { opacity: 0; }
  50%      { opacity: 0.08; }
}

/* -- Tab bar buttons (finance/billing): unified active feel -------- */

button[style*="borderBottom"][style*="var(--accent)"] {
  position: relative;
  transition:
    color var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth);
}

/* -- Lab orders / clickable KPI buttons: outline polish ------------ */

.card.interactive[style*="outline"] {
  transition:
    outline var(--duration-fast) var(--ease-smooth),
    transform var(--duration-base) var(--ease-smooth),
    box-shadow var(--duration-base) var(--ease-smooth);
}

/* ════════════════════════════════════════════════════════════════════
   V-1/4 (ADR-048) — Numeric typography utilities (Hybrid Stripe layer)
   ════════════════════════════════════════════════════════════════════
   Canonical hook for "numbers are the product" surfaces (Billing /
   Finance / Analytics / KPI values). Two utility classes:

     .num-tabular  — apply to ANY element displaying digits in a
                     column or grid where column-alignment matters
                     (table cells, KPI deltas, count badges).
     .num-currency — like .num-tabular plus right-alignment via
                     text-align: end (RTL-aware: end = visual right
                     in RTL too because the cell box itself is RTL-
                     anchored). Use on currency display elements
                     (invoice totals, expense amounts, balances).

   Both classes set font-variant-numeric: tabular-nums for modern
   browsers AND font-feature-settings: 'tnum' 1 as the older-browser
   fallback. Effective on Latin numerals (0-9) — Arabic-Indic digits
   (٠-٩) already have uniform width natively.

   Existing scattered inline declarations (lines 4691, 5391, 5565,
   5596, 5804, 6427, 7214) stay as legacy patterns until Wave 2
   V-Billing / V-Finance / V-Analytics tracks migrate them to these
   utility classes. The .data-table td.numeric pattern is the most
   important pre-existing hook (line 5389-5393); .num-tabular on
   the wrapper is the new alternative when a cell isn't inside a
   .data-table primitive.

   If Cairo's Latin numerals look insufficient in production
   tabular columns (subjective owner check during V-Billing), a
   V-1/4 follow-up can add `--font-num-stack: Inter, ...` as a token
   + apply it on .num-tabular. Not shipped now to avoid the extra
   Google Fonts network request without proof-of-need.
   ════════════════════════════════════════════════════════════════════ */

.num-tabular {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

.num-currency {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  text-align: end;
}

/* ════════════════════════════════════════════════════════════════════
   V-1/5 (ADR-048) — Hero card soft gradient (Hybrid Stripe layer)
   ════════════════════════════════════════════════════════════════════
   Stripe's signature: a barely-there diagonal accent tint on hero
   surfaces (Finance Overview's `+12,400 ج.م` card, Analytics
   waterfall hero, Dashboard greeting card). Goes from accent-tinted
   in the corner to transparent at midpoint — adds depth without
   ornamentation.

   Strength controlled by --tint-hero-strength token (default 4%).
   V-1/3 dark themes may override the token if 4% is invisible on
   dark surfaces.

   Compose with existing .card classes — .tint-hero only sets the
   background-image, so .card.section + .tint-hero stacks bg + tint.
   ════════════════════════════════════════════════════════════════════ */

.tint-hero {
  background-image: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) var(--tint-hero-strength), transparent) 0%,
    transparent 50%);
}

/* -- Money/numeric values: tabular-nums everywhere ----------------- */

[style*="fontWeight: 900"][style*="fontSize: 2"],
[style*="fontWeight:900"][style*="fontSize:2"] {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.015em;
}

/* -- Invoice summary boxes (billing right rail) -------------------- */

.card.section[style*="height:'fit-content'"],
.card[style*="height:'fit-content'"] {
  position: sticky;
  top: 14px;
}

/* -- Aging buckets / progress visualization ------------------------ */

[data-aging-bar] {
  transition: width var(--duration-slow) var(--ease-out);
}

/* -- Filter date preset pills (Finance Overview) ------------------- */

button[style*="DATE_PRESETS"],
.date-preset {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

/* -- Payment status badges (paid/partial/unpaid/overdue) ----------- */

.chip.success,
.chip.danger,
.chip.warning,
.chip.info {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

/* -- Empty states inside cards (billing/finance lists) ------------- */

.card .empty-state,
.card [style*="padding: 40"][style*="textAlign"][style*="center"] {
  padding: var(--space-8) !important;
  color: var(--text-tertiary);
}

/* -- Mobile billing/finance: tighter spacing ----------------------- */

@media (max-width: 720px) {
  .grid.cols-4 {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ════════════════════════════════════════════════════════════════════
   PHASE 6 -- Settings + Admin + WhatsApp premium polish
   ════════════════════════════════════════════════════════════════════ */

/* -- Settings sub-tabs (general/staff/etc) ------------------------- */

[class*="settings-tab"],
.settings-nav button {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

/* -- Doctor profile cards: gentle interactive feel ----------------- */

.card[style*="textAlign: 'center'"]:has(.avatar.lg),
.card[style*='textAlign: "center"']:has(.avatar.lg) {
  transition:
    transform var(--duration-base) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-base) var(--ease-smooth);
}

.card[style*="textAlign: 'center'"]:has(.avatar.lg):hover,
.card[style*='textAlign: "center"']:has(.avatar.lg):hover {
  transform: translateY(var(--lift-2));
  box-shadow: var(--shadow);
  border-color: color-mix(in srgb, var(--accent) 18%, var(--border));
}

/* -- Avatar large (used in settings doctor cards) ------------------ */

.avatar.lg {
  width: 64px;
  height: 64px;
  font-size: var(--text-title);
  border-radius: 18px;
  position: relative;
  overflow: hidden;
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
  transition: transform var(--duration-base) var(--ease-spring);
}

.avatar.lg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.16), transparent 55%);
  pointer-events: none;
  border-radius: inherit;
}

.card:hover .avatar.lg {
  transform: scale(1.05);
}

/* The settings team list retired its data-table on 2026-07-28: doctors AND
   staff are PersonCards at every width (see set-person-grid), so the old
   table-to-card phone flip has nothing left to convert. */

/* -- Admin clinics list rows ---------------------------------------- */

[data-clinic-row],
.clinic-row {
  transition:
    background var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

[data-clinic-row]:hover,
.clinic-row:hover {
  background: var(--bg-hover);
}

/* -- WhatsApp message preview bubble polish ------------------------ */

[data-whatsapp-preview] {
  background: linear-gradient(135deg, #dcf8c6 0%, #d4f3b8 100%);
  border-radius: var(--radius);
  padding: 12px 16px;
  position: relative;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}

/* -- WhatsApp template variable chips ------------------------------ */

[data-tmpl-var],
.template-var {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, #25D366 12%, transparent);
  color: #128C7E;
  font-weight: 700;
  font-size: var(--text-xs);
  font-family: monospace;
  margin: 0 2px;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

[data-tmpl-var]:hover,
.template-var:hover {
  background: color-mix(in srgb, #25D366 22%, transparent);
  transform: scale(1.05);
}

/* -- Settings form fields: focus polish ---------------------------- */

/* -- Toggle/switch components polish ------------------------------- */

input[type="checkbox"] + label {
  transition:
    background var(--duration-fast) var(--ease-smooth);
}

/* -- Admin permission grid items ----------------------------------- */

[data-permission-grid] > label {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-smooth);
}

[data-permission-grid] > label:hover {
  background: var(--bg-hover);
}

/* Mobile .card.kpi stacking rule removed (KpiCard P5): family retired. */

/* ════════════════════════════════════════════════════════════════════
   PHASE 7 -- Appointments + Marketing + Inventory + Patient-file
   ════════════════════════════════════════════════════════════════════ */

/* -- Appointments: calendar day cells polish ----------------------- */

[data-day-cell],

/* -- Appointment row in calendar pill ------------------------------ */

/* -- Marketing post cards -------------------------------------------- */
[class*="mk-post"],
[class*="mk-card"] {
  transition:
    transform var(--duration-base) var(--ease-smooth),
    box-shadow var(--duration-base) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth);
}

[class*="mk-post"]:hover,
[class*="mk-card"]:hover {
  transform: translateY(var(--lift-2));
  box-shadow: var(--shadow);
}

/* -- Marketing status chips ---------------------------------------- */

/* -- Inventory: progress bar with shimmer animation ---------------- */

[data-inventory-progress] .progress-bar,
.inventory-bar {
  position: relative;
  transition: width var(--duration-slow) var(--ease-out);
}

/* -- Inventory: low/critical stock rows ---------------------------- */

[data-stock-row][data-status="critical"] {
  background: color-mix(in srgb, var(--danger) 6%, var(--bg-card));
  animation: pulseSoft 3s ease-in-out infinite;
}

[data-stock-row][data-status="low"] {
  background: color-mix(in srgb, var(--warning) 6%, var(--bg-card));
}

/* Hover Wave 3c: the .inventory-status / [data-stock-status] badge family
   (base + scale-on-hover) was DEAD - zero JSX consumers anywhere (the
   inventory screen renders StatusChip instead). Deleted. */

/* -- New appointment modal: time slot picker ----------------------- */

[data-time-slot] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-card);
  cursor: pointer;
  font-weight: 600;
  font-size: var(--text-sm);
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

[data-time-slot]:hover {
  border-color: var(--accent);
  transform: translateY(var(--lift-1));
}

[data-time-slot].selected,
[data-time-slot][aria-pressed="true"] {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* .card.kpi active-filter rule removed (KpiCard P5): MetricTile owns .is-active. */

/* -- Mobile: marketing calendar full-width ------------------------- */

@media (max-width: 720px) {
}

/* ════════════════════════════════════════════════════════════════════
   PHASE 8A -- Premium Skeleton Loaders
   ════════════════════════════════════════════════════════════════════
   Replace blank/"..." loading states with shimmering placeholders.

   Usage:
     <div class="skeleton-text">    -> single line text placeholder
     <div class="skeleton-title">   -> bold title placeholder
     <div class="skeleton-number">  -> KPI value placeholder
     <div class="skeleton-circle">  -> avatar/icon placeholder
     <div class="skeleton-bar">     -> button/bar placeholder
     <div class="skeleton-row">     -> table row placeholder
   ──────────────────────────────────────────────────────────────────── */

.skeleton-text,
.skeleton-title,
.skeleton-number,
.skeleton-circle,
.skeleton-bar,
.skeleton-row {
  /* V-1/5 (ADR-048) — accent-tinted shimmer via §7 tokens */
  background: linear-gradient(
    90deg,
    var(--shimmer-base) 0%,
    var(--shimmer-highlight) 50%,
    var(--shimmer-base) 100%
  );
  background-size: 200% 100%;
  animation: shimmer var(--shimmer-duration) ease-in-out infinite;
  border-radius: var(--radius-sm);
  color: transparent !important;
  user-select: none;
  pointer-events: none;
  display: inline-block;
}

/* Variants */

.skeleton-text   { height: 12px; width: 70%;  border-radius: var(--radius-xs); }

.skeleton-title  { height: 18px; width: 50%;  border-radius: 5px; }

.skeleton-number { height: 28px; width: 110px; border-radius: var(--radius-sm); }

.skeleton-circle { height: 40px; width: 40px; border-radius: 50%; }

.skeleton-bar    { height: 32px; width: 100%; border-radius: var(--radius-sm); }

.skeleton-row    { height: 48px; width: 100%; border-radius: var(--radius-sm); display: block; margin-bottom: 8px; }

/* Group: skeleton stack for a fake "list" */

.skeleton-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
}

/* When inside a card, skeletons inherit the card's padding */

.card .skeleton-text,
.card .skeleton-title,
.card .skeleton-number {
  display: block;
  margin-bottom: 8px;
}

/* V-9/2: Kanban-shape skeleton (Reception Suspense fallback). 3 columns
   side-by-side, each with a title shimmer + N card shimmers. Reads from
   --shimmer-base / --shimmer-highlight via inherited .skeleton-* classes. */

.skeleton-kanban {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  width: 100%;
}

.skeleton-kanban-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  padding: var(--space-3);
  background: var(--bg-elevated);
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

.skeleton-kanban-card {
  height: 72px;
  width: 100%;
  border-radius: var(--radius-sm);
  display: block;
  background-color: var(--shimmer-base);
  background-image: linear-gradient(90deg, transparent, var(--shimmer-highlight), transparent);
  background-size: 200% 100%;
  /* BUG FIX (W-REVEAL 2026-07-17): was `skeleton-shimmer` - a keyframe that
     does not exist, so the kanban skeleton showed a static gray that never
     swept. The whole .skeleton-* family animates via the ONE `shimmer`. */
  animation: shimmer var(--shimmer-duration) infinite linear;
}

@media (max-width: 768px) {
  .skeleton-kanban { grid-template-columns: 1fr; }
}

/* V-9/2: Form-shape skeleton (NewAppointment / NewInvoice fallback). Two-
   column responsive grid of label+input pairs. */

.skeleton-form {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  width: 100%;
}

.skeleton-form-field {
  display: flex;
  flex-direction: column;
}

@media (max-width: 768px) {
  .skeleton-form { grid-template-columns: 1fr; }
}

/* Subtle skeleton in dark mode (Luxe theme) */

[data-theme-mode="dark"] .skeleton-text,
[data-theme-mode="dark"] .skeleton-title,
[data-theme-mode="dark"] .skeleton-number,
[data-theme-mode="dark"] .skeleton-circle,
[data-theme-mode="dark"] .skeleton-bar,
[data-theme-mode="dark"] .skeleton-row {
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.04) 0%,
    rgba(255, 255, 255, 0.10) 50%,
    rgba(255, 255, 255, 0.04) 100%
  );
  background-size: 200% 100%;
}

/* Reduced motion: stop shimmer */

@media (prefers-reduced-motion: reduce) {
  .skeleton-text,
  .skeleton-title,
  .skeleton-number,
  .skeleton-circle,
  .skeleton-bar,
  .skeleton-row {
    animation: none;
    background: var(--bg-subtle);
  }
}

/* ════════════════════════════════════════════════════════════════════
   PHASE 8B -- Custom Confirm Dialog (replaces native confirm())
   ════════════════════════════════════════════════════════════════════ */

.confirm-dialog {
  /* Centering via the parent .modal-backdrop grid (display: grid + place-
     items: center). The earlier manual fixed-positioning pattern was
     clobbered by the modalIn keyframe ending in `transform: translateY(0)
     scale(1)` (animation-fill-mode: both), which overrode the centering
     transform and offset the dialog by half its size. Matches the
     .modal/.modal-box pattern at L980. ADR-064 amendment. */
  width: min(420px, calc(100vw - 32px));
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 24px 22px 20px;
  text-align: center;
  position: relative; /* z-index honoured without escaping the grid layout */
  z-index: 1001;
  animation: modalIn var(--modal-in) var(--modal-in-ease) var(--modal-lead) backwards;
  will-change: transform, opacity;
}

.confirm-dialog__icon {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  margin: 0 auto 14px;
  display: grid;
  place-items: center;
  font-size: 28px;
  font-weight: 900;
  font-family: var(--font-sans, 'IBM Plex Sans Arabic', sans-serif);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15);
  animation: scaleIn var(--duration-base) var(--ease-spring) both;
  animation-delay: 80ms;
}

.confirm-dialog__title {
  font-size: 18px;
  font-weight: 900;
  color: var(--text-primary);
  margin-bottom: 8px;
  letter-spacing: -0.01em;
}

.confirm-dialog__message {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: 20px;
}

.confirm-dialog__actions {
  display: flex;
  gap: var(--space-2-5);
  justify-content: stretch;
}

.confirm-dialog__actions .btn {
  flex: 1;
  min-height: 40px;
  font-weight: 700;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth),
    box-shadow var(--duration-fast) var(--ease-smooth),
    filter var(--duration-fast) var(--ease-smooth);
}

.confirm-dialog__actions .btn:hover:not(:disabled) {
  filter: brightness(1.06);
  transform: translateY(var(--lift-1));
}

.confirm-dialog__actions .btn:active:not(:disabled) {
  transform: translateY(0);
}

/* Backdrop blur enhancement for confirm */

.modal-backdrop:has(.confirm-dialog) {
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  background: rgba(0, 0, 0, 0.4);
}

/* Mobile */

@media (max-width: 480px) {
  .confirm-dialog {
    width: calc(100vw - 24px);
    padding: 20px 18px 16px;
  }
  .confirm-dialog__title { font-size: 16px; }
  .confirm-dialog__message { font-size: 12.5px; }
}

/* ════════════════════════════════════════════════════════════════════
   PHASE 8C -- Premium Toast Notification (top-center, clean look)
   U-10/1: ToastHost stacks up to MAX_VISIBLE toasts in a flex column;
   the .toast-stack wrapper owns positioning + centering, .toast items
   are flow-laid inside.
   ════════════════════════════════════════════════════════════════════ */

/* The stack wrapper handles fixed positioning + RTL-agnostic centering.
   Explicit left/right (not inset-inline-*) per the Phase 8C hot-fix
   reasoning: in RTL the logical pair resolves opposite to what
   centering needs and breaks the centered alignment. */

.toast-stack {
  position: fixed;
  top: 20px;
  left: 50%;
  right: auto;
  bottom: auto;
  transform: translateX(-50%);
  z-index: 3000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  align-items: center;
  pointer-events: none;
  max-width: 100vw;
}

/* Reset legacy Phase 1 .toast positioning (line ~1432 block, dead but
   still cascades — was previously suppressed by Phase 8C !important
   rules; without those it leaks fixed/bottom/inset-inline-end into the
   stack). Toasts inside the stack are flow-laid. */

.toast-stack > .toast {
  position: static;
  inset: auto;
  pointer-events: auto;
  max-width: 100%;
}

.toast {
  min-width: 280px;
  max-width: 480px;
  padding: 12px 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow:
    0 10px 30px rgba(0, 0, 0, 0.12),
    0 4px 12px rgba(0, 0, 0, 0.06);
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  font-weight: 600;
  color: var(--text-primary);
  animation: toastSlideIn 320ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes toastSlideIn {
  from { opacity: 0; transform: translateY(-16px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0)      scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}

/* V-9/4: Type variants — subtle tint (color-mix) + colored left-border
   + colored icon chip. Stronger visual differentiation than icon-only;
   left-border survives at-a-glance scanning even when message is long.
   border-inline-start picks the right edge in RTL automatically. */

.toast.success {
  background: color-mix(in srgb, var(--success) 5%, var(--bg-elevated));
  border-inline-start: 3px solid var(--success);
}

.toast.danger {
  background: color-mix(in srgb, var(--danger) 6%, var(--bg-elevated));
  border-inline-start: 3px solid var(--danger);
}

.toast.warning {
  background: color-mix(in srgb, var(--warning) 6%, var(--bg-elevated));
  border-inline-start: 3px solid var(--warning);
}

.toast.info {
  background: color-mix(in srgb, var(--info) 5%, var(--bg-elevated));
  border-inline-start: 3px solid var(--info);
}

.toast.success .toast-icon { background: var(--success-soft); color: var(--success); }

.toast.danger  .toast-icon { background: var(--danger-soft);  color: var(--danger); }

.toast.warning .toast-icon { background: var(--warning-soft); color: var(--warning); }

.toast.info    .toast-icon { background: var(--info-soft);    color: var(--info); }

/* Icon sized down, cleaner */

.toast-icon {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  flex-shrink: 0;
}

.toast-msg {
  flex: 1;
  min-width: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.5;
  color: var(--text-primary);
  padding-inline: 2px;
}

.toast-close {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  background: transparent;
  border: none;
  color: var(--text-tertiary);
  cursor: pointer;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth);
}

.toast-close:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

/* Toast action button — Undo / retry / view (U-3, foundation for U-10) */

.toast-action {
  font: inherit;
  font-size: var(--text-meta);
  font-weight: 700;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  cursor: pointer;
  flex-shrink: 0;
  white-space: nowrap;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    border-color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

.toast-action:hover {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

.toast-action:active {
  transform: scale(0.96);
}

.toast-action:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus, 0 0 0 3px var(--ring-color, color-mix(in srgb, var(--accent) 35%, transparent)));
}

/* Mobile: full width minus margins (applied to the stack wrapper now;
   individual toasts inherit width via the flex column). */

@media (max-width: 480px) {
  .toast-stack {
    top: 12px;
    width: calc(100vw - 24px);
  }
  .toast-stack > .toast {
    width: 100%;
    min-width: 0;
    max-width: none;
  }
}

/* ════════════════════════════════════════════════════════════════════
   WHATSAPP INBOX -- WhatsApp-style chat UI
   ════════════════════════════════════════════════════════════════════ */

.wa-inbox-grid {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: var(--space-4);
  align-items: start;
  height: calc(100vh - 280px);
  min-height: 540px;
}

/* -- Conversations list (left) -------------------------------------- */

.wa-inbox-list {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.wa-inbox-list-header {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  background: var(--bg-elevated);
}

.wa-inbox-list-body {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}

.wa-inbox-list-footer {
  padding: 10px 14px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

.wa-inbox-unread-badge {
  background: #25D366;
  color: #fff;
  font-size: var(--text-micro);
  font-weight: 800;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  box-shadow: 0 2px 6px color-mix(in srgb, #25D366 35%, transparent);
}

/* -- Thread row -------------------------------------------------------- */

/* -- Chat thread (right) ---------------------------------------------- */

.wa-inbox-thread {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

/* WhatsApp chat background pattern */

/* ==================================================================== */

/* WhatsApp-EXACT chat (PROJECT_WHATSAPP.md §12 - owner 2026-07-18).     */

/* ONE system, shared by the conversation modal AND the patient-file     */

/* WhatsApp tab (both render window.WhatsAppThreadView).                 */

/*                                                                       */

/* Reproduces the real WhatsApp conversation: the beige/dark textured    */

/* ground, grouped bubbles with a tail on the FIRST of each run,         */

/* centered date separators, time + delivery ticks, and the input bar.   */

/*                                                                       */

/* Bilingual for free: outgoing sits at the writing-direction END        */

/* (flex-end = LEFT in Arabic RTL, RIGHT in English LTR) exactly as      */

/* WhatsApp mirrors itself, so only the TAIL geometry needs [dir] rules. */

/*                                                                       */

/* The palette lives on BOTH `.wa-chat-modal` (so the header + composer, */

/* which are SIBLINGS of the scroll body, inherit it) and               */

/* `.wa-thread-body` (so the patient-file tab, which has no modal        */

/* wrapper, still gets it). Keyed on data-theme-mode so every dark theme */

/* gets WhatsApp's dark palette, not just one.                          */

/* ==================================================================== */

.wa-chat-modal,
.wa-thread-body {
  --wa-ground: #efeae2;
  --wa-sent: #d9fdd3;
  --wa-recv: #ffffff;
  --wa-txt: #111b21;
  --wa-meta: rgba(11,20,26,0.45);
  --wa-bar: #f0f2f5;
  --wa-bar-icon: #54656f;
  --wa-input-bg: #ffffff;
}

[data-theme-mode="dark"] .wa-chat-modal,
[data-theme-mode="dark"] .wa-thread-body {
  --wa-ground: #0b141a;
  --wa-sent: #005c4b;
  --wa-recv: #202c33;
  --wa-txt: #e9edef;
  --wa-meta: rgba(233,237,239,0.55);
  --wa-bar: #202c33;
  --wa-bar-icon: #8696a0;
  --wa-input-bg: #2a3942;
}

.wa-thread-body {
  flex: 1;
  overflow-y: auto;
  padding: 14px 6%;
  display: flex;
  flex-direction: column;
  gap: 0;
  background-color: var(--wa-ground);
  /* Two offset dot layers = a soft textured ground in WhatsApp's spirit.
     Deliberately OUR texture, not Meta's doodle artwork (that asset is
     theirs); the ground COLOR + bubbles are what read as WhatsApp. */
  background-image:
    radial-gradient(circle, rgba(0,0,0,0.030) 0 1px, transparent 1.6px),
    radial-gradient(circle, rgba(0,0,0,0.020) 0 1px, transparent 1.6px);
  background-size: 26px 26px, 26px 26px;
  background-position: 0 0, 13px 13px;
  min-height: 0;
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
}

[data-theme-mode="dark"] .wa-thread-body {
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.038) 0 1px, transparent 1.6px),
    radial-gradient(circle, rgba(255,255,255,0.026) 0 1px, transparent 1.6px);
}

/* -- Date separator ---------------------------------------------------- */

/* Centered pill at every local-day boundary (اليوم / أمس / date), which is */

/* what lets the per-bubble meta stay time-only like the real app.         */

.wa-date-sep {
  display: flex;
  justify-content: center;
  margin: 12px 0 10px;
}

.wa-date-sep span {
  background: var(--wa-recv);
  color: var(--wa-meta);
  font-size: var(--text-meta);
  font-weight: 600;
  padding: 5px 12px;
  border-radius: var(--radius-md);
  box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.13);
}

/* -- Message rows + grouping ------------------------------------------- */

/* `flex-end` for outgoing is what mirrors the whole chat between Arabic   */

/* and English with zero extra rules: end = LEFT in RTL, RIGHT in LTR.     */

.wa-msg-wrap {
  display: flex;
  width: 100%;
  margin-top: 2px;
}

.wa-msg-wrap.run-start {
  margin-top: 10px;
}

.wa-msg-wrap:first-child {
  margin-top: 0;
}

.wa-msg-wrap.sent {
  justify-content: flex-end;
}

.wa-msg-wrap.received {
  justify-content: flex-start;
}

.wa-msg-bubble {
  max-width: 75%;
  padding: 6px 9px 6px;
  font-size: 14.2px;
  line-height: 1.45;
  color: var(--wa-txt);
  background: var(--wa-recv);
  box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.13);
  position: relative;
  word-break: break-word;
  border-radius: var(--radius-md);
  animation: waBubbleIn 180ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.wa-msg-bubble.sent {
  background: var(--wa-sent);
}

.wa-msg-bubble.received {
  background: var(--wa-recv);
}

/* -- Tail (first bubble of a run only) --------------------------------- */

/* WhatsApp squares the bubble's OUTER top corner and flares a small       */

/* triangle out of it. Which side is "outer" flips with the language:      */

/*   RTL: sent sits LEFT  -> tail on the left  · received RIGHT -> right   */

/*   LTR: sent sits RIGHT -> tail on the right · received LEFT  -> left    */

/* Continuation bubbles in a run stay fully rounded and tuck underneath.   */

[dir="rtl"] .wa-msg-bubble.sent.tail,
[dir="ltr"] .wa-msg-bubble.received.tail {
  border-top-left-radius: 0;
}

[dir="rtl"] .wa-msg-bubble.received.tail,
[dir="ltr"] .wa-msg-bubble.sent.tail {
  border-top-right-radius: 0;
}

.wa-msg-bubble.tail::after {
  content: "";
  position: absolute;
  top: 0;
  width: 9px;
  height: 13px;
}

.wa-msg-bubble.sent.tail::after {
  background: var(--wa-sent);
}

.wa-msg-bubble.received.tail::after {
  background: var(--wa-recv);
}

/* left-side tail: flares up-and-out from the squared top-left corner */

[dir="rtl"] .wa-msg-bubble.sent.tail::after,
[dir="ltr"] .wa-msg-bubble.received.tail::after {
  left: -8px;
  clip-path: polygon(100% 0, 100% 62%, 0 0);
}

/* right-side tail: the mirror image */

[dir="rtl"] .wa-msg-bubble.received.tail::after,
[dir="ltr"] .wa-msg-bubble.sent.tail::after {
  right: -8px;
  clip-path: polygon(0 0, 0 62%, 100% 0);
}

@keyframes waBubbleIn {
  from { opacity: 0; transform: translateY(3px); }
  to   { opacity: 1; transform: translateY(0); }
}

.wa-msg-text {
  white-space: pre-wrap;
  word-break: break-word;
}

/* Media sits flush inside the bubble the way WhatsApp frames it. */

.wa-msg-media {
  display: block;
  max-width: 100%;
  max-height: 260px;
  border-radius: var(--radius-sm);
}

.wa-msg-doc {
  color: var(--wa-txt);
  font-weight: 600;
  text-decoration: underline;
}

.wa-msg-meta {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 3px;
  margin-top: 1px;
  font-size: var(--text-xs);
  color: var(--wa-meta);
  direction: ltr;
}

.wa-msg-ticks {
  font-size: var(--text-base);
  color: var(--wa-meta);
  letter-spacing: -3px;
}

.wa-msg-ticks.read {
  color: #53bdeb;
}

.wa-msg-ticks.sending {
  letter-spacing: 0;
  font-size: var(--text-meta);
}

/* -- Failed send -------------------------------------------------------
   A message Meta refused - or one that died before it ever left the browser -
   used to render an ordinary check, byte-identical to a delivered one, so a
   dead message wore the costume of a successful one until a full page reload.
   It now owns a colour, states its reason, and offers the way out. */

.wa-msg-bubble.failed {
  background: var(--danger-soft);
  box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.13), inset 0 0 0 1px var(--danger);
}

.wa-msg-bubble.failed.tail::after {
  background: var(--danger-soft);
}

.wa-msg-ticks.failed {
  color: var(--danger);
  letter-spacing: 0;
}

.wa-msg-error {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  font-size: var(--text-xs);
  line-height: 1.55;
  color: var(--danger);
}

.wa-msg-error__why {
  flex: 1 1 auto;
  min-width: 0;
}

.wa-msg-error__retry {
  flex: 0 0 auto;
  border: 1px solid var(--danger);
  background: transparent;
  color: var(--danger);
  border-radius: var(--radius-sm);
  padding: 2px 8px;
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition:
    background 140ms var(--ease-out, ease),
    color 140ms var(--ease-out, ease);
}

.wa-msg-error__retry:hover:not(:disabled) {
  background: var(--danger);
  color: #fff;
}

.wa-msg-error__retry:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* -- Empty thread ------------------------------------------------------
   M11 (Audit 08-01): renamed from .wa-empty — that name is ALSO the generic
   rich-empty shell (ADR-060 §2, defined later in this file), so the later
   block silently won the cascade and the chat thread rendered with a dashed
   border + the wrong palette. The thread owns its own vocabulary now. */

.wa-thread-empty {
  margin: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
  padding: 28px 16px;
}

.wa-thread-empty-badge {
  width: 62px;
  height: 62px;
  border-radius: 50%;
  background: var(--wa-recv);
  color: var(--wa-meta);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}

.wa-thread-empty-title {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--wa-txt);
}

.wa-thread-empty-hint {
  font-size: var(--text-meta);
  color: var(--wa-meta);
  max-width: 250px;
  line-height: 1.5;
}

/* -- Composer (WhatsApp input bar) ------------------------------------- */

/* Pill input that grows with the text + a circular send button. The       */

/* decorative attach / emoji / mic buttons WhatsApp shows are deliberately */

/* ABSENT - we can't perform any of them, and a dead control misleads.     */

.wa-composer {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  padding: 8px 12px;
  background: var(--wa-bar, var(--bg-elevated));
  flex-shrink: 0;
}

.wa-composer-input {
  flex: 1;
  min-width: 0;
  resize: none;
  border: none;
  outline: none;
  background: var(--wa-input-bg, var(--bg-card));
  color: var(--wa-txt, var(--text-primary));
  border-radius: var(--radius-xl);
  padding: 10px 16px;
  font-family: inherit;
  font-size: var(--text-base);
  line-height: 1.45;
  max-height: 120px;
  overflow-y: auto;
  box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.08);
}

.wa-composer-input::placeholder {
  color: var(--wa-meta, var(--text-tertiary));
}

.wa-send-btn {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  min-width: 46px;
  padding: 0;
  border-radius: 50%;
  background: #00a884;
  color: #fff;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

.wa-send-btn:hover:not(:disabled) {
  background: #017561;
}

.wa-send-btn:active:not(:disabled) {
  transform: scale(0.94);
}

.wa-send-btn:disabled {
  background: var(--wa-bar-icon, var(--text-tertiary));
  opacity: 0.45;
  cursor: not-allowed;
}

/* -- Chat header (conversation modal) ---------------------------------- */

/* Identity on the leading side, real actions on the trailing side. We     */

/* substitute WhatsApp's call/video icons with the two things we ACTUALLY  */

/* support (open the patient file, delete the conversation).              */

.wa-chat-head {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 9px 12px;
  background: var(--wa-bar);
  color: var(--wa-txt);
  flex-shrink: 0;
  min-height: 59px;
}

.wa-chat-head__av {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #6a7175;
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-md);
  font-weight: 700;
  flex-shrink: 0;
}

.wa-chat-head__id {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.wa-chat-head__name {
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--wa-txt);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wa-chat-head__sub {
  font-size: var(--text-meta);
  color: var(--wa-bar-icon);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wa-chat-head__actions {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-inline-start: auto;
}

.wa-chat-head__back,
.wa-chat-head__close,
.wa-chat-head__act {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--wa-bar-icon);
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-md);
  transition: background var(--duration-fast) var(--ease-smooth);
}

.wa-chat-head__back:hover,
.wa-chat-head__close:hover,
.wa-chat-head__act:hover:not(:disabled) {
  background: color-mix(in srgb, var(--wa-bar-icon) 18%, transparent);
}

.wa-chat-head__act:disabled {
  opacity: 0.38;
  cursor: not-allowed;
}

/* The back glyph points toward the reading start: right in Arabic (RTL),
   left in English (LTR) - one icon, mirrored. */

[dir="ltr"] .wa-chat-head__back svg {
  transform: scaleX(-1);
}

/* Desktop keeps a trailing close; the phone gets WhatsApp's leading back
   arrow instead (a full-screen chat is "backed out of", not "dismissed"). */

.wa-chat-head__back { display: none; }

.wa-chat-head__close { display: inline-flex; }

@media (max-width: 47.99em) {
  .wa-chat-head__back { display: inline-flex; }
  .wa-chat-head__close { display: none; }
}

/* ==================================================================== */

/* CENTRAL chat shell (window.ChatModal) - owner 2026-07-18.             */

/* ONE geometry for EVERY conversation: WhatsApp, Modir, Clinic Room.    */

/* Width comes from the standard size token (md = 560px) via .rm-card--md */

/* - it is NOT overridden here, so a chat is exactly as wide as the rest  */

/* of the app's modals.                                                  */

/*                                                                       */

/* Mobile is a ~94dvh SHEET, not full-screen: the sliver of backdrop +    */

/* the drag handle are what tell you it can be pulled down to leave. The  */

/* pull-down itself is GRAB-ONLY (handle/header) - see chat-modal.jsx for */

/* why an at-top rule cannot work on a bottom-pinned chat.               */

/*                                                                       */

/* Rules are (0,2,0) so they beat the base .rm-* regardless of order.     */

/* ==================================================================== */

.rm-card.chat-modal {
  height: 94dvh;
  max-height: 94dvh;
}

/* Header + messages + composer are the three flex rows; only the middle
   one scrolls, so the bars stay pinned exactly like a real chat app. */

.chat-modal .rm-body {
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* The non-scrolling grab zone the dismiss gesture binds to. */

.chat-modal__head {
  flex-shrink: 0;
}

/* Close path for lib/swipe-to-dismiss.js - it clicks this so the sheet's
   OWN React teardown runs. Zero-size + out of flow: never visible. */

.chat-modal__close {
  position: absolute;
  width: 0;
  height: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

@media (min-width: 48em) {
  .rm-card.chat-modal {
    height: min(80vh, 680px);
    max-height: calc(100dvh - 4rem);
  }
}

/* Native (Capacitor) safe areas. The sheet is bottom-anchored, so its composer
   lands ON the home indicator, and on a tall notch the header can reach the
   status bar. Solved ONCE here for all three chats — this replaces the
   per-drawer `.crd-drawer` fix the Clinic Room used to carry alone, which is
   exactly the duplication this refactor exists to remove. */

body.is-native .rm-card.chat-modal {
  height: calc(94dvh - var(--safe-area-inset-top, env(safe-area-inset-top, 0px)));
  max-height: calc(94dvh - var(--safe-area-inset-top, env(safe-area-inset-top, 0px)));
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* -- WhatsApp SKIN ----------------------------------------------------- */

/* Geometry now comes from .chat-modal above; this block is only the       */

/* WhatsApp look (its ground colour replaces the glass surface). Keeping   */

/* the skin separate is what lets WhatsApp stay unmistakably WhatsApp      */

/* while sharing every dimension with Modir + Clinic Room.                */

.rm-card.wa-chat-modal {
  background: var(--wa-ground);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.wa-thread-reply-hint {
  padding: 4px 14px 10px;
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  background: var(--bg-elevated);
  flex-shrink: 0;
  border-bottom-left-radius: var(--radius-lg);
  border-bottom-right-radius: var(--radius-lg);
}

/* ==================================================================== */

/* WhatsApp redesign 2026-07-18 — ONE screen + modals.                  */

/* The conversations table lives on the central DataTable; these styles */

/* only cover the bits with no central class: the unread row accent,    */

/* the settings-modal rows, and the compose preview bubble.             */

/* ==================================================================== */

/* Unread conversation row — a subtle start-border accent (mirrors the  */

/* billing settlement-row tone convention, no new color system).        */

.wa-conv-row--unread td:first-child,
.wa-conv-row--unread .dt-card {
  box-shadow: inset 3px 0 0 var(--accent);
}

.wa-conv-row--unread .dt-card-title { font-weight: 800; }

/* Settings modal — connection row */

.wa-settings-conn {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-subtle);
}

.wa-settings-conn.is-connected {
  border-color: color-mix(in srgb, var(--success) 40%, var(--border));
  background: color-mix(in srgb, var(--success) 8%, var(--bg-subtle));
}

/* Template-readiness badge under the connected number (ADR-257): a central
   StatusChip + a short explanatory hint. Reassures the user their templates
   are under Meta review, then flips to "ready" once approved. */

.wa-tpl-status {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: 8px;
}

.wa-tpl-status__hint {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.45;
}

/* Settings modal — automation rule rows (label + Toggle) */

.wa-settings-rules {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.wa-settings-rule {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}

.wa-settings-rule:hover { border-color: var(--accent); }

.wa-settings-rule:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Compose modal — the message preview bubble sits in a chat-tinted tray */

.wa-compose-preview {
  padding: var(--space-3);
  border-radius: var(--radius);
  background:
    repeating-linear-gradient(45deg, rgba(0,0,0,0.012) 0 1px, transparent 1px 14px),
    #e5ddd5;
  max-height: 200px;
  overflow-y: auto;
}

[data-theme-mode="dark"] .wa-compose-preview {
  background:
    repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 14px),
    #0a1117;
}

/* -- Mobile inbox layout --------------------------------------------- */

@media (max-width: 900px) {
  .wa-inbox-grid {
    grid-template-columns: 1fr;
    height: auto;
  }
  .wa-inbox-list {
    max-height: 320px;
  }
  .wa-inbox-thread {
    height: 60vh;
  }
}

/* ════════════════════════════════════════════════════════════════════
   WhatsApp Confirmation Link Realtime Highlights
   When a patient confirms/cancels/reschedules via a Modirify link,
   the matching appointment card in Reception/Appointments briefly
   highlights to alert the staff.
   ════════════════════════════════════════════════════════════════════ */

.wa-just-changed {
  animation: waChangedPulse 1.6s ease-out 3, waChangedFade 8s ease-out forwards;
  position: relative;
}

.wa-just-changed--confirm {
  --wa-action-color: #25D366;
  --wa-action-soft: rgba(37, 211, 102, 0.16);
}

.wa-just-changed--cancel {
  --wa-action-color: #ef4444;
  --wa-action-soft: rgba(239, 68, 68, 0.16);
}

.wa-just-changed--reschedule {
  --wa-action-color: #f59e0b;
  --wa-action-soft: rgba(245, 158, 11, 0.16);
}

@keyframes waChangedPulse {
  0%   { box-shadow: 0 0 0 0    var(--wa-action-soft, rgba(37,211,102,.20)); }
  50%  { box-shadow: 0 0 0 8px  transparent; }
  100% { box-shadow: 0 0 0 0    transparent; }
}

@keyframes waChangedFade {
  0%   { background-color: var(--wa-action-soft, rgba(37,211,102,.12)); }
  60%  { background-color: var(--wa-action-soft, rgba(37,211,102,.06)); }
  100% { background-color: inherit; }
}

/* Kanban card variant: leans on existing card styles */

.kanban-card.wa-just-changed {
  border-color: var(--wa-action-color, #25D366) !important;
  border-width: 2px;
}

/* Table row variant for appointments list */

tr.wa-just-changed > td {
  background-color: var(--wa-action-soft, rgba(37,211,102,.10)) !important;
}

/* Confirmation badge displayed inside the affected card */

/* ── ONE kanban-card badge family (رقم ٦, owner 2026-07-18: «سيستم موحد») ──
   Replaces two private families (.wa-confirm-badge in-flow + the absolutely
   positioned .kanban-waiting-badge). One placement (in-flow at the card top,
   badges stack), one chrome, tone = meaning:
     --success  the patient CONFIRMED (channel on the tooltip)
     --waiting  parked for anesthesia / x-ray (card also gets .waiting-extra wash)
     --flash    transient via-Modirify event (8s window, slide-in) */

.kanban-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  font-size: var(--text-micro);
  font-weight: 800;
  margin-bottom: 8px;
  letter-spacing: 0.01em;
  animation: slideDown 360ms cubic-bezier(.16,1,.3,1) both;
}

.kanban-badge--success {
  background: color-mix(in srgb, var(--success) 18%, var(--bg-elevated));
  color: var(--success);
  border: 1px solid color-mix(in srgb, var(--success) 40%, transparent);
}

.kanban-badge--waiting {
  background: #ea580c;
  color: #fff;
  box-shadow: 0 2px 8px color-mix(in srgb, #ea580c 35%, transparent);
}

.kanban-badge--flash {
  background: var(--wa-action-color, #25D366);
  color: #fff;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--wa-action-color, #25D366) 35%, transparent);
}

/* Small tag for table rows (less intrusive) */

@media (prefers-reduced-motion: reduce) {
  .wa-just-changed { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════
   SPRINT 1 — DASHBOARD 2.0 + FOUNDATION COMPONENTS
═══════════════════════════════════════════════════════════════ */

/* Dashboard Hero (.dash-hero*) — RETIRED R3 Phase 3 (2026-07-11). All 14 screens
   moved to the central ScreenHeader; the ONE bespoke exception (the clinical
   patient header) owns .pf-hero* now. See app/src/ui/screen-header.jsx +
   the .pf-hero block above. */

/* Primary metric tile — 3 large cards under the hero */

.metric-tile {
  position: relative;
  padding: 22px 24px;
  border-radius: 18px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  transition: transform var(--duration-base) var(--ease-out), box-shadow var(--duration-base), border-color var(--duration-base);
  cursor: pointer;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.metric-tile::before {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(135deg, var(--tile-tint, transparent) 0%, transparent 60%);
  opacity: 0.6;
  pointer-events: none;
}

/* Hover Wave 3c (owner: "inventory tiles hover too hard"): the INTERACTIVE
   tile hover was the app's strongest (lift-3 -6px + 40% border) while
   read-only tiles hover gently - clickable-filter screens (inventory,
   patients) looked like a different system. Softened to the standard card
   language (lift-2 + 25% border, glow eased); still a touch stronger than
   .is-readonly below so clickable tiles keep reading as clickable. */

@media (hover: hover) {
.metric-tile:hover {
  transform: translateY(var(--lift-2));
  box-shadow: 0 8px 20px color-mix(in srgb, var(--tile-color, var(--accent)) 10%, transparent);
  border-color: color-mix(in srgb, var(--tile-color, var(--accent)) 25%, var(--border));
}
}

.metric-tile-head { display: flex; align-items: center; gap: var(--space-2-5); }

.metric-tile-icon {
  width: 36px; height: 36px; border-radius: var(--radius);
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--tile-color, var(--accent)) 12%, var(--bg-elevated));
  color: var(--tile-color, var(--accent));
  font-size: 18px;
  flex-shrink: 0;
}

.metric-tile-label {
  font-size: var(--text-meta); font-weight: 700; color: var(--text-tertiary);
  letter-spacing: 0.04em; text-transform: uppercase;
}

.metric-tile-value {
  /* V-Mobile/KpiTypo (MOBILE_ARCHITECTURE §8.2) — fluid clamp scales
     the KPI number with viewport instead of an abrupt step at the
     mobile breakpoint. Mobile 22px → tablet ~28px → desktop 36px.
     The owner reported the previous fixed 28px on mobile felt too
     big for single-digit / 4-digit currency values. */
  font-size: clamp(1.375rem, 2vw + 1rem, 2.25rem);  /* 22px → 36px */
  font-weight: 900; color: var(--text-primary); line-height: 1;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}

.metric-tile-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }

.metric-tile-trend {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: var(--text-meta); font-weight: 800;
  padding: 3px 10px; border-radius: var(--radius-pill);
}

.metric-tile-trend.up    { background: color-mix(in srgb, var(--success) 12%, transparent); color: var(--success); }

.metric-tile-trend.down  { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); }

.metric-tile-trend.flat  { background: color-mix(in srgb, var(--text-tertiary) 10%, transparent); color: var(--text-tertiary); }

.metric-tile-hint { font-size: var(--text-xs); color: var(--text-tertiary); font-weight: 600; }

/* ────────────────────────────────────────────────────────────────
   KpiCard unification (Phase 0, 2026-07-09) — opt-in capabilities on
   the ONE canonical .metric-tile so every legacy stat family
   (.kpi / .card.kpi / .sa-kpi / .wa-stat / .bourse-stat / .ann-anl-kpi
   / .import-tile) + the ~64 bespoke inline "number in a box" divs can
   migrate onto it WITHOUT a look-alike. All additive — an un-migrated
   tile is untouched. Driven by app/src/ui/metric-tile.jsx props.
   ──────────────────────────────────────────────────────────────── */

/* Unit / suffix after the headline figure (٪ · ج.م · جلسة). Sits on the
   value baseline, de-emphasised so the number stays the hero. Absorbs the
   hand-rolled "value + unit" concatenation (ai-card suffix, wa quota). */

.metric-tile-value .metric-tile-suffix {
  font-size: 0.6em; font-weight: 700; color: var(--text-tertiary);
  margin-inline-start: 4px; letter-spacing: 0;
}

/* Value tone — colour the NUMBER itself (not just the icon/trend). Absorbs
   .pf2-stat-card (always-danger) + .bourse-stat--danger/success where the
   figure's colour carries the meaning. */

.metric-tile-value.mt-good    { color: var(--success); }

.metric-tile-value.mt-bad     { color: var(--danger); }

.metric-tile-value.mt-neutral { color: var(--text-tertiary); }

/* Semantic trend tone — decouple colour from arrow direction so
   "المصروفات ↓" or "المديونية ↓" reads GREEN (down = good). The arrow still
   comes from trend.dir; the colour from trend.tone. */

.metric-tile-trend.mt-t-good    { background: color-mix(in srgb, var(--success) 12%, transparent);       color: var(--success); }

.metric-tile-trend.mt-t-bad     { background: color-mix(in srgb, var(--danger) 12%, transparent);        color: var(--danger); }

.metric-tile-trend.mt-t-neutral { background: color-mix(in srgb, var(--text-tertiary) 10%, transparent); color: var(--text-tertiary); }

/* Leading accent stripe — absorbs the .mk-kpi / .sa-kpi "colour bar" idiom
   without a separate family, using the tile's own --tile-color. */

.metric-tile.mt-bar { border-inline-start: 3px solid var(--tile-color, var(--accent)); }

/* Matching skeleton (SkeletonMetricTile) — a .metric-tile-shaped placeholder
   so a tile does NOT reflow on load. Suppress the tint overlay + pointer. */

.metric-tile.is-skeleton { pointer-events: none; }

.metric-tile.is-skeleton::before { display: none; }

/* Section header */

.section-head {
  display: flex; align-items: center; justify-content: space-between;
  margin: 28px 0 12px;
  gap: var(--space-2-5);
}

.section-head h2 { font-size: var(--text-base); font-weight: 800; color: var(--text-primary); letter-spacing: 0.02em; margin: 0; display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

.section-head .section-sub { font-size: var(--text-meta); color: var(--text-tertiary); font-weight: 500; }

.section-head .section-actions { display: flex; gap: var(--space-1-5); align-items: center; flex-shrink: 0; }

/* The lead can shrink so a long TRANSLATED title wraps inside the card instead
   of shoving the action button off the inline-end edge. */

.section-head__lead { min-width: 0; }

/* ── SectionCard — the bordered block a screen body is built from ──────────
   `.card` is a bare class typed by hand at ~303 sites, and the heading row on
   top of it was re-typed inline 18 more times in four spacing dialects. The
   component (window.SectionCard) composes THIS heading anatomy — which already
   existed with one consumer — so no rival vocabulary enters the app. The only
   new rules are the in-card margin reset and the mobile stack. */

.section-card > .section-head { margin: 0 0 12px; }

/* p-0 = the LIST-CARD recipe (analytics W2, 2026-07-19): padded head + a
   hairline under it, body rows edge-to-edge. One central shape for every
   "card with a table/list inside" instead of each screen re-typing the
   14px/20px header strip. */

.section-card.p-0 > .section-head { margin: 0; padding: 14px 20px; border-bottom: 1px solid var(--border); }

/* The bare head (window.SectionHead) sits ABOVE a list of cards, so it keeps
   breathing room from the block before it but not the class's original 28px —
   these shelves stack inside one tab, not across a dashboard. */

.section-head--bare { margin: 0 0 12px; }

/* ── SECTION RHYTHM (owner-caught 2026-07-19: «ليه بلوك الملاحظات لازق كده؟»)
   The gap between page sections was every card's own `mb-16` — which works
   right up until a section ISN'T a card. The dental chart is a bare `.dc-shell`
   grid, so it contributed no bottom margin at all and the block under it sat
   flush against it. Spacing that lives on the CHILD can always be forgotten by
   the next child that isn't one.
   So the rule lives on the CONTAINER — but scoped to the sections that DON'T
   already declare their own, so no existing screen's spacing shifts by a pixel.
   Cards keep `mb-16`; anything else now gets the same gap for free. */

.page-content.reveal-stagger > *:not(.card):not(.screen-header):not([class*="mb-"]) {
  margin-bottom: 16px;
}

/* ── .section-item — a ROW inside a SectionCard ────────────────────────────
   Owner 2026-07-19: «الوثائق والأشعة والوصفات العناوين برة — حطهم جوه الكارت
   المركزي». Once a shelf's heading moves INSIDE the card, its entries can no
   longer be cards themselves (a card inside a card reads as a mistake). They
   become rows on a quieter surface: same radius family, no shadow, a border
   that only separates. One class for all three shelves — a prescription, an
   x-ray and a document are the same THING here (a stored record with a title,
   a meta line and a kebab), so they get one vocabulary, not three. */

.section-item-stack { display: flex; flex-direction: column; gap: var(--space-2-5); }

.section-item {
  background: var(--bg-subtle);
  border: 1px solid color-mix(in srgb, var(--border) 88%, transparent);
  border-radius: 10px;
  padding: 12px 14px;
}

.section-item > .section-head { margin: 0; }

.section-item > .section-head h2 { font-size: var(--text-sm); }

/* A row's kebab is an icon button, not a mini-CTA — it must not inherit the
   section action's tinted pill or every row would shout. */

.section-item > .section-head .section-actions > .btn.icon-btn,
.section-item > .section-head .section-actions > .row-action-menu > .btn {
  background: transparent;
  border-color: transparent;
  color: var(--text-tertiary);
}

.section-item-body { margin-top: 10px; }

/* ── The mini-CTA (owner 2026-07-19: «عاوز توضيح للزراير أكتر جوه الكروت
   بحيث كأنه mini cta والمستخدم ياخد باله») ─────────────────────────────────
   A section's action was a `btn ghost sm` — text the same weight as the card's
   own body, no edge, invisible until you hunt for it. It is the ONE thing you
   can DO with this card, so it now reads as a small, tinted, bordered button:
   the screen's primary language at a quarter of its volume. One rule, every
   card on every screen, so a section action never has to be styled per site. */

.section-head .section-actions > .btn {
  font-size: var(--text-meta);
  font-weight: 700;
  padding-inline: 11px;
  min-height: 32px;
  border-radius: var(--radius-md);
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  transition: background-color .16s ease, border-color .16s ease, color .16s ease;
}

.section-head .section-actions > .btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
}

.section-head .section-actions > .btn:disabled { opacity: .55; }

/* A RECORDING mini-CTA - same shape, danger hue, filled - for the dictation mic
   while the microphone is open (owner 2026-07-28: «خليها متلونة ومعمولة بنفس
   الديزاين بتاع البوتون اللي في الكارت عشان تبان للطبيب»). It is filled rather
   than tinted because "your microphone is live" is the one state in this slot
   that must be impossible to miss from arm's length, and it matches the red the
   quiet mic uses in a composer row so the cue means one thing everywhere. */

.section-head .section-actions > .btn.is-recording {
  color: #fff;
  background: var(--danger, #d64545);
  border-color: var(--danger, #d64545);
}

[data-theme="luxe"] .section-head .section-actions > .btn.is-recording { color: #0b1020; }

.section-head .section-actions > .btn.is-recording svg { animation: voice-pulse 1.4s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .section-head .section-actions > .btn.is-recording svg { animation: none; }
}

/* A DESTRUCTIVE mini-CTA borrows the same shape in the danger hue. */

.section-head .section-actions > .btn.is-danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 26%, transparent);
  background: color-mix(in srgb, var(--danger) 7%, transparent);
}

/* An UNSAVED-WORK mini-CTA — same shape, FILLED, because it is the one section
   action the user must not walk away from (W-BUTTONS ج٣, 2026-07-28: the dental
   chart's save). The tinted default is right for "you may do this"; a pending
   save is "you have changes that are not stored yet", so it earns the solid
   accent while staying the same object in the same slot. Any section with
   unsaved state uses this rather than smuggling a `btn primary` into the head —
   the tint rule outranks `.btn.primary` on specificity anyway, so a bare
   `primary` would silently flatten and the urgency would be lost. */

.section-head .section-actions > .btn.is-pending {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* Same theme carve-out `.btn.primary` already declares — the luxe accent is
   light, so white-on-accent is unreadable there. Reusing its tokens instead of
   hand-mixing a hover keeps one accent story. */

[data-theme="luxe"] .section-head .section-actions > .btn.is-pending { color: #0b1020; }

.section-head .section-actions > .btn.is-pending:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

@media (prefers-reduced-motion: reduce) {
  .section-head .section-actions > .btn { transition: none; }
}

@media (max-width: 768px) {
  /* Owner-caught 2026-07-19: stacking the action UNDER the title read as a
     broken row («ليه هي مكسورة مش في الجنب؟»). The head stays a ROW at every
     width — a mini-CTA is small enough to sit beside its title on a phone, and
     the title wraps into the space it leaves instead of pushing it off. */
  .section-card > .section-head,
  .section-head--bare { gap: var(--space-2); }
  .section-head .section-actions > .btn { min-height: 34px; padding-inline: 10px; }
}

/* Polished empty state with action button */

.empty-state.v2 {
  display: flex; flex-direction: column; align-items: center;
  padding: 36px 20px; text-align: center; gap: var(--space-3);
}

.empty-state.v2 .empty-state-icon {
  width: 64px; height: 64px; border-radius: 18px;
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-elevated));
  color: var(--accent);
  display: grid; place-items: center; font-size: 28px;
  margin-bottom: 4px;
}

.empty-state.v2 .empty-state-title { font-size: var(--text-md); font-weight: 800; color: var(--text-primary); }

.empty-state.v2 .empty-state-text { font-size: var(--text-sm); color: var(--text-tertiary); max-width: 320px; line-height: 1.5; }

.empty-state.v2 .empty-state-action { margin-top: 6px; }

/* R3 Phase 3 (2026-07-11): the .dash-rise staggered entrance + the .dash-hero
   shell it animated are RETIRED (Rule 9). The ONE entrance system is now
   .reveal-stagger (reveal-utils.js); the sole remaining bespoke hero (the
   clinical patient header) owns its own .pf-hero shell + pfHeroIn entrance. */

/* Mobile responsiveness */

@media (max-width: 768px) {
  .metric-tile { padding: 18px 18px; }
  /* V-Mobile/KpiTypo (§8.2): mobile-specific override removed — the
     fluid clamp on .metric-tile-value handles mobile scaling now
     (22px at <= 480px viewport). The compact variant uses the same
     fluid scaling so both stay visually aligned across screens. */
}

/* ═══════════════════════════════════════════════════════════════
   SPRINT 1.2 — RECEPTION 2.0 (Drag-drop kanban polish)
═══════════════════════════════════════════════════════════════ */

/* Drag affordance: cursor + subtle indicator on hover */

.kanban-card[draggable="true"] {
  cursor: grab;
  user-select: none;
}

.kanban-card[draggable="true"]:active { cursor: grabbing; }

/* While the card is being dragged */

.kanban-card.dragging {
  opacity: 0.4;
  transform: scale(0.97);
  cursor: grabbing;
  box-shadow: 0 12px 28px color-mix(in srgb, var(--accent) 22%, transparent);
  transition: opacity var(--duration-fast), transform var(--duration-fast), box-shadow var(--duration-fast);
}

/* The column where a card can be dropped (hovered over) */

.kanban-col.drop-target {
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-elevated));
  outline: 2px dashed color-mix(in srgb, var(--accent) 55%, transparent);
  outline-offset: -2px;
  transition: background var(--duration-fast), outline-color var(--duration-fast);
}

.kanban-col.drop-target .kanban-col-hdr {
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-subtle)) !important;
}

/* Forbidden drop target (same column or invalid transition) */

.kanban-col.drop-forbidden {
  background: color-mix(in srgb, var(--danger) 5%, var(--bg-elevated));
  outline: 2px dashed color-mix(in srgb, var(--danger) 45%, transparent);
  outline-offset: -2px;
}

/* Drag-handle dot shown in top-right corner of card on hover */

.kanban-card .drag-hint {
  position: absolute;
  top: 6px; inset-inline-end: 8px;
  font-size: var(--text-meta); color: var(--text-tertiary);
  opacity: 0;
  transition: opacity var(--duration-fast);
  pointer-events: none;
}

.kanban-card:hover .drag-hint { opacity: 0.55; }

/* Smooth kanban col animation when cards rearrange */

.kanban-col .kanban-cards-stack {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-2-5); min-height: 90px;
  transition: background var(--duration-fast);
}

/* Drop-into-column hint shown when column is empty + targeted */

.kanban-empty.drop-active {
  border: 2px dashed color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  color: var(--accent);
  font-weight: 700;
}

/* Reception KPI row uses the metric-tile system but a compact variant.
   V-Mobile/KpiTypo (§8.2) — compact also uses fluid clamp so the
   single-digit values (e.g., "1 مرضى جدد") don't dominate the card
   on mobile. Mobile 18px → tablet ~22px → desktop 28px. */

.metric-tile.compact { padding: 14px 16px; }

.metric-tile.compact .metric-tile-value {
  font-size: clamp(1.125rem, 1.5vw + 0.75rem, 1.75rem);  /* 18px → 28px */
}

.metric-tile.compact .metric-tile-icon { width: 30px; height: 30px; font-size: var(--text-md); }

.metric-tile.compact .metric-tile-label { font-size: var(--text-xs); }

/* Dense tier (KpiCard, 2026-07-09) — the SMALL size for MODALS + tight/secondary
   summaries (invoice-detail / payable-detail money-triads). Same card family, just
   a denser box + a one-line value so the currency never wraps in a narrow modal
   column (owner flagged the compact size felt too heavy inside a modal). */

.metric-tile.dense { padding: 10px 12px; gap: var(--space-1-5); }

.metric-tile.dense .metric-tile-head { gap: 7px; }

.metric-tile.dense .metric-tile-value {
  font-size: clamp(0.95rem, 0.6vw + 0.8rem, 1.15rem);  /* ~15px -> 18px, single line */
  white-space: nowrap;
}

.metric-tile.dense .metric-tile-icon { width: 26px; height: 26px; font-size: var(--text-sm); border-radius: 9px; }

.metric-tile.dense .metric-tile-label { font-size: 10.5px; }

.metric-tile.dense .metric-tile-suffix { font-size: 0.62em; }

/* ── The ONE KPI grid (owner picked the Reception model, 2026-07-09) ───────────
   The card LOOK was unified, but the GRID around it was not: KPI rows used a
   jungle of l-grid--fixed-3 / l-grid--fixed-4 / grid-cols-4 / inline grids, each
   with a different column count + a different mobile rule. That is why a 3-tile
   row (patients/finance) stretched each card to a THIRD of a wide screen (owner:
   "why are the cards big?") and why mobile was 1-up on some screens, 2-up on
   others. `.kpi-grid` is the single arrangement, modelled on Reception:
   auto-fit COMPACT tiles (fixed max width, so few tiles never stretch oversized)
   that wrap naturally; on mobile exactly 2-up, with a lonely last tile spanning
   the row so an odd count stays balanced (2 + 2 + 1-wide). Reception keeps its
   own origin class; every other KPI row adopts this. */

.kpi-grid {
  display: grid;
  gap: var(--space-3);
  /* Fill the row (no side gap) like Reception's l-grid--sm. Few-tile rows still
     stretch wider than Reception's 8-tile row (3 compact tiles cannot fill a wide
     row — it is arithmetic); the wide-card look is fixed by the horizontal card
     layout below, not by the grid. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  align-items: stretch;
}

@media (max-width: 768px) {
  .kpi-grid { grid-template-columns: 1fr 1fr; gap: var(--space-2); }
  .kpi-grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* Spring landing animation — applied to a card for 350ms after it's
   dropped into a new column. The cubic-bezier y-value > 1 produces
   the overshoot bounce (scale peaks ~1.04 around 60% of the duration
   before settling at 1). Reception toggles `.is-just-landed` via
   justLandedId state in onDropCol. */

@keyframes kanban-card-land {
  from { transform: scale(0.94); }
  to   { transform: scale(1); }
}

.kanban-card.is-just-landed {
  animation: kanban-card-land 350ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Reduced motion preference */

@media (prefers-reduced-motion: reduce) {
  .kanban-card.dragging { transform: none; }
  .kanban-card.is-just-landed { animation: none; }
  .kanban-col.drop-target { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════
   U-2 PRIMITIVE SUPPLEMENT — Tabs
   ════════════════════════════════════════════════════════════════════
   Adds the count badge, size, and full-width variants consumed by
   the Tabs JSX primitive in app/src/ui/tabs.jsx. The base .tab /
   .tabs styles from earlier in this file remain canonical and are
   not touched. New tokens (--text-sm, --text-2xs) come from
   tokens.css (ADR-017).
*/

.tabs-full { width: 100%; }

.tabs-full .tab { flex: 1 1 0; justify-content: center; }

.tabs-sm .tab,
.tab.sm {
  min-height: 36px;
  padding: 7px 12px;
  font-size: var(--text-sm);
}

.tab .tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  margin-inline-start: 6px;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  /* Chrome Wave D (2026-07-08, owner): the count number matches the tab TITLE
     color. `inherit` tracks .tab's color in BOTH states — muted when inactive
     (--text-tertiary), accent when active — so the number is never a different
     hue from the label beside it. */
  color: inherit;
  font-size: var(--text-xs);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.tab.active .tab-count {
  /* Keep the soft accent pill; the number color inherits the active tab's
     accent title color (no explicit override — that is the point of Wave D). */
  background: var(--accent-soft);
}

/* W6 (online booking): pulse the count pill when a tab has pending items
   (e.g. reception "طلبات الحجز"). The accent fill + halo make the waiting
   number the at-a-glance alert (the app has no notification bell). */

.tab .tab-count.tab-count-pulse {
  background: var(--accent);
  color: #fff;
  animation: tabCountPulse 1.8s ease-in-out infinite;
}

@keyframes tabCountPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(14, 165, 183, 0.0); }
  50%      { box-shadow: 0 0 0 4px var(--accent-soft, rgba(14, 165, 183, 0.30)); }
}

@media (prefers-reduced-motion: reduce) {
  .tab .tab-count.tab-count-pulse { animation: none; }
}

/* CountBadge (Round 3 Phase 0a): the ONE count pill (app/src/ui/count-badge.jsx)
   that retires the scattered per-strip badge styles. Same look as .tab-count;
   default color inherits the surrounding text (an active tab's badge matches
   its accent title, Wave D). Tones stand alone when needed; pulse reuses the
   tab-count attention animation. */

.count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: inherit;
  font-size: var(--text-xs);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* ── ConnectionChip (window.ConnectionChip) ──────────────────────────────
   The ONE "is this integration live?" indicator, rendered in a ScreenHeader
   subtitle. Promoted here from `screens/marketing/marketing-redesign.css`
   (`.mkx-conn`) when WhatsApp needed the same treatment: that file is a LAZY
   route chunk, so the class only existed once Marketing had been visited.
   Eager + central = both screens always look identical. */

.conn-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-sm);
  font-weight: 700;
}

.conn-chip.is-on { color: var(--success); }

.conn-chip.is-off { color: var(--danger); }

.conn-chip.is-loading { color: var(--text-tertiary); }

.conn-chip__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}

.conn-chip.is-on .conn-chip__dot { animation: connChipPing 1.9s ease-out infinite; }

.conn-chip__detail {
  font-weight: 600;
  color: var(--text-tertiary);
}

@keyframes connChipPing {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 55%, transparent); }
  70%  { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .conn-chip.is-on .conn-chip__dot { animation: none; }
}

.count-badge--accent { background: var(--accent-soft); color: var(--accent); }

.count-badge--danger { background: var(--danger-soft); color: var(--danger); }

/* Solid fill — for a count that must be UNMISSABLE rather than ambient, i.e.
   an unread badge. The soft tones read as decoration next to a bold name (the
   owner's "مش باينة"); WhatsApp's unread pill is solid-on-white for exactly
   this reason. Added to the primitive instead of hand-styling a call site. */

.count-badge--solid { background: var(--accent); color: #fff; }

.count-badge--pulse {
  background: var(--accent);
  color: #fff;
  animation: tabCountPulse 1.8s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .count-badge--pulse { animation: none; }
}

/* SegmentedControl (Round 3 Phase 0b): moved off the component's inline styles
   into themeable CSS - a bordered group of 2-5 mutually-exclusive options; the
   active option fills with the accent (or an opt-in per-option --seg-active).
   Same look as before, now overridable by theme/context (was un-themeable). */

.seg-control {
  display: inline-flex;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.seg-control__item {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1-5);
  padding: 9px 10px;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  white-space: nowrap;
  background: transparent;
  color: var(--text-secondary);
  transition: background 0.15s, color 0.15s;
}

.seg-control__item + .seg-control__item { border-inline-start: 1px solid var(--border); }

.seg-control__item.is-active { background: var(--seg-active, var(--accent)); color: #fff; }

.seg-control__item:disabled { cursor: default; opacity: 0.55; }

.seg-control.is-disabled .seg-control__item { cursor: default; }

.seg-control--sm .seg-control__item { padding: 6px 8px; font-size: var(--text-meta); }

/* Touch minimum on phones. 9px padding + 13px text lands the default segment at
   42px — two pixels under the 44px target, on a control whose whole job is
   being tapped. Measured on the الإشعارات mode picker; fixed HERE rather than
   there, because all 14 consumers have the same thumb. The `--sm` variant is
   deliberately dense (tight toolbars) and keeps its own size. */

@media (max-width: 768px) {
  .seg-control:not(.seg-control--sm) .seg-control__item { min-height: 44px; }
}

.seg-control__badge { margin-inline-start: 4px; font-size: var(--text-xs); font-weight: 800; opacity: 0.85; }

/* DateControl (Round 3 Phase 0c): one date control, 3 modes. 'range' delegates
   to the shared DateRangeFilter; 'month'/'day' are the compact variants that
   replace the one-off month inputs + the appointments day-nav. */

.date-control { display: inline-flex; align-items: center; gap: var(--space-2); }

.date-control--month .date-control__label {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 42px;
  min-width: 132px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--state-duration, .15s) var(--state-ease, ease),
              background var(--state-duration, .15s) var(--state-ease, ease);
}

.date-control--month .date-control__label:hover {
  border-color: var(--accent);
  background: var(--state-hover-surface, var(--bg-subtle));
}

.date-control__month-text {
  color: var(--text-primary);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The native month input is an INVISIBLE, click-through overlay over the pill:
   the user reads the derived "MonthName YYYY" label, and a click anywhere on the
   pill opens the native month picker (the direct far-jump). The arrows step. */

.date-control__month-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  opacity: 0;
  outline: none;
  cursor: pointer;
}

.date-control--day { gap: var(--space-1); }

.date-control__today {
  height: 38px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: pointer;
  transition: background var(--state-duration) var(--state-ease),
              border-color var(--state-duration) var(--state-ease);
}

.date-control__today:hover { background: var(--state-hover-surface); border-color: var(--accent); }

.date-control__day-label {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--text-primary);
  min-width: 116px;
  text-align: center;
}

/* Reveal (Round 3 Phase 0d): staggered section entrance. Wrap a screen's top
   sections in .reveal-stagger; each direct child floats in with an increasing
   delay (one after another). Pairs with useCountUp for KPI numbers
   (app/src/lib/reveal-utils.js). Reduced-motion shows everything instantly. */

/* Timing matches the v2 mockup exactly: floatIn .6s cubic-bezier(.16,1,.3,1)
   with a blur-lift, and a 0.10s gap between staggered sections (mockup delays
   .04/.14/.24/.34) so the sequential reveal is actually felt, not a blur. */

@keyframes floatIn {
  from { opacity: 0; transform: translateY(14px); filter: blur(5px); }
  to   { opacity: 1; transform: none;             filter: blur(0); }
}

.reveal { animation: floatIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) both; }

.reveal-stagger > * { animation: floatIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) both; }

.reveal-stagger > *:nth-child(1) { animation-delay: 0.04s; }

.reveal-stagger > *:nth-child(2) { animation-delay: 0.14s; }

.reveal-stagger > *:nth-child(3) { animation-delay: 0.24s; }

.reveal-stagger > *:nth-child(4) { animation-delay: 0.34s; }

.reveal-stagger > *:nth-child(5) { animation-delay: 0.44s; }

.reveal-stagger > *:nth-child(6) { animation-delay: 0.54s; }

.reveal-stagger > *:nth-child(n+7) { animation-delay: 0.60s; }

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .reveal-stagger > * { animation: none; }
}

/* ScreenHeader (Round 3 Phase 1): the ONE slim per-screen header — greeting +
   context on the RTL-right, the date filter + primary action on the RTL-left.
   Sits under the ScreenTabBar. Replaces the .dash-hero band (which duplicated
   the topbar title). See app/src/ui/screen-header.jsx. */

/* ══════════════════════════════════════════════════════════════════
   W-HDR (owner 2026-07-17) — the header layout is DECLARED, not emergent.
   ══════════════════════════════════════════════════════════════════
   Was: display:flex + flex-wrap:wrap + justify-content:space-between.
   That made the CTA's position a function of TITLE LENGTH, not of the
   viewport: when the lead grew past the free space the actions wrapped,
   and `space-between` on a 1-item line resolves to flex-START — so the
   button landed on the RTL-RIGHT, under the title, instead of the left.
   Measured live at 660px: «الجرد» -> button left, same row;
   «تذكيرات واتساب التلقائية للمرضى» -> button right, second row. Same
   viewport, only the string differed. That IS the reported
   "some buttons on the side, some below".
   Now: an explicit 2-column grid above the device line, an explicit
   1-column stack below it. `minmax(0, 1fr)` lets a long title ellipsize
   or wrap INSIDE its own column instead of shoving the CTA around, and
   the CTA can never migrate across the header on its own.
   min-height keeps every screen's header the same height whether or not
   it carries a CTA (settings/updates have none) - the owner's
   "الهيدر يحجز مكانه دايمًا": 46px is the action height (see :8505).
   ══════════════════════════════════════════════════════════════════ */

.screen-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px 16px;
  min-height: 46px;
  margin-bottom: 18px;
}

/* Align the header with the screen's content on EVERY screen (owner 2026-07-09:
   the finance greeting sat flush-right vs its cards). The header self-pads
   horizontally by the .page-content inset (32px) so a header in a bespoke scroll
   container (e.g. finance's .finance-scroll, which pads only 14px while its cards
   sit inside a nested .page-content) lines up with the content below - EXCEPT
   when the header is already a direct child of .page-content (reception/billing),
   which supplies that inset itself. One central rule, correct anywhere. */

/* W-HDR: the SAME inset the content below uses (see --screen-inset). A
   header that is already a direct child of .page-content inherits that
   inset from its parent, so it zeroes its own. */

.screen-header { padding-inline: var(--screen-inset); }

.page-content > .screen-header { padding-inline: 0; }

.screen-header__lead { min-width: 0; }

/* W-HERO (owner 2026-07-17): per-tab identity — tinted icon chip before the
   tab name. Same accent-tint recipe as .inv-item-row / the modal title icon
   so identity surfaces share one accent language. */

.screen-header__lead--identity { display: flex; align-items: center; gap: var(--space-3); }

.screen-header__lead-text { min-width: 0; }

/* ============================================================
   W-ICON (owner 2026-07-17) — the ONE identity-chip treatment.
   ============================================================
   Owner picked the "glass" variant from an 8-way premium mockup:
   a soft accent gradient + a top inner-light + a tinted drop shadow,
   so the chip reads as a raised piece of glass instead of a flat tint.

   ONE recipe, three sizes, driven by --chip-size + --chip-radius:
     .id-chip            44px  -> the screen header (identity announcement)
     .id-chip--sm        28px  -> the topbar title (the persistent compass)
     .id-chip--nav       20px  -> the sidebar's ACTIVE row only
   The sidebar deliberately does NOT chip every row: 12 stacked glass
   squares would out-shout the content AND destroy the meaning of
   "active". Rest rows stay bare glyphs (see .nav-item .icon-box).

   Everything derives from --accent, so all 12 themes + dark mode work
   with no per-theme overrides. inset light + shadow are accent-tinted,
   never black, so the chip never dirties a dark surface.
   ============================================================ */

.id-chip,
.screen-header__icon {
  --chip-size: 44px;
  --chip-radius: 13px;
  width: var(--chip-size);
  height: var(--chip-size);
  border-radius: var(--chip-radius);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--accent);
  background:
    linear-gradient(145deg,
      color-mix(in srgb, var(--accent) 22%, var(--bg-elevated)),
      color-mix(in srgb, var(--accent) 6%, var(--bg-elevated)));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
  box-shadow:
    0 2px 8px color-mix(in srgb, var(--accent) 18%, transparent),
    inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent);
}

.id-chip--sm { --chip-size: 28px; --chip-radius: 9px; }

.id-chip--nav { --chip-size: 20px; --chip-radius: 7px; box-shadow: none; }

@media (prefers-reduced-transparency: reduce) {
  .id-chip, .screen-header__icon { box-shadow: none; }
}

.screen-header__title {
  font-size: var(--text-title);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.4px;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

/* The name sits inline in the greeting - same weight + colour as the greeting
   (matches the v2 mockup "صباح الخير د/ محمد 👋"; NOT accent-tinted). */

.screen-header__name { color: inherit; }

/* The subtitle row holds TWO kinds of information with opposite overflow
   needs: prose (`__sub`, may be ellipsized) and live state (`__status`, may
   not). Desktop keeps them on one wrapping line — identical to the old
   hand-written "prose · chip · chip" subtitle, with the separator restored
   below — and the ≤768px block stacks them. See app/src/ui/screen-header.jsx. */

.screen-header__subrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: 2px;
  min-width: 0;
}

.screen-header__sub {
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--text-tertiary);
}

.screen-header__status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Screens used to write the ' · ' themselves inside the subtitle Fragment.
   Now that the chips are their own slot, the separator is the header's job —
   so a screen can never ship a dangling '·' when its chip renders null. */

.screen-header__sub + .screen-header__status::before {
  content: '·';
  color: var(--text-tertiary);
  font-size: 13.5px;
  line-height: 1;
}

/* (Removed 2026-07-09: the data-screen "info line" variant. The owner reversed
   the info-line decision - EVERY screen now shows the central greeting header
   (greeting + name + date), so the sub-line is always the date caption under a
   greeting, never a lone info line.) */

.screen-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  flex-wrap: wrap;
}

.screen-header__date { display: flex; align-items: center; }

/* Anatomy W1-F6-b7: the date filter now lives in the header date-slot. The
   trigger's default in-card bottom margin (below) is nulled here so it aligns
   with the primary action instead of sinking below the baseline. */

.date-range-trigger { margin-bottom: 12px; }

.screen-header__date .date-range-trigger { margin-bottom: 0; }

/* Anatomy W1-F6-b8 (owner root fix): while the popover is OPEN, lift the WHOLE
   reveal-stagger section that hosts it above later sections. floatIn's frozen
   `filter: blur(0)` end-frame makes each revealed section its own z-index:auto
   stacking context, so the taller calendar popover was painted BEHIND the next
   section (the KPI cards). The old per-trigger z-index only re-ranked inside
   the section; this lifts the section itself. Central - every screen with a
   header date-slot OR a standalone filter block (analytics) is covered. */

.reveal-stagger > *:has(.date-range-trigger.is-open),
.screen-header:has(.date-range-trigger.is-open) { position: relative; z-index: 70; }

/* Anatomy W1-F6-b8: the ONE premium date-filter trigger (owner v2 pill). A
   boxed calendar glyph + a small "الفترة الزمنية" caption over the bold active
   value + a chevron. Theme-safe tokens only; identical on every screen. */

.date-range-btn {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  min-height: 46px;
  padding: 6px 12px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 13px;
  cursor: pointer;
  font-family: inherit;
  box-shadow: var(--shadow-sm);
  transition: border-color var(--state-duration, .15s) var(--state-ease, ease),
              box-shadow var(--state-duration, .15s) var(--state-ease, ease);
}

.date-range-btn:hover { border-color: var(--accent); }

.date-range-btn.is-open { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

.date-range-btn__icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 9px;
  background: var(--accent-soft);
  color: var(--accent-text);
  flex-shrink: 0;
}

.date-range-btn__text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.2; min-width: 0; }

.date-range-btn__label { font-size: 10.5px; font-weight: 600; color: var(--text-tertiary); }

.date-range-btn__value { font-size: 13.5px; font-weight: 700; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

.date-range-btn__chev { color: var(--text-tertiary); margin-inline-start: auto; flex-shrink: 0; transition: transform .15s ease; }

.date-range-btn.is-open .date-range-btn__chev { transform: rotate(180deg); }

/* Anatomy W2: the ONE list-screen toolbar (app/src/ui/screen-toolbar.jsx). A
   card-less tray: row 1 (search flexes + تصفية + view) · row 2 status chips
   with counts (D2) · row 3 the removable active-filter chips bar. Selection mode
   swaps the whole tray for an accent action bar in place (no layout jump). All
   theme tokens. */

.screen-toolbar { display: flex; flex-direction: column; gap: var(--space-2-5); margin-bottom: 16px; }

.screen-toolbar .stb-row { display: flex; align-items: center; gap: var(--space-2-5); flex-wrap: wrap; }

.screen-toolbar .stb-search { flex: 1; min-width: 220px; }

.stb-filter { position: relative; gap: var(--space-1-5); }

.stb-filter__count {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px;
  padding: 0 5px; margin-inline-start: 4px; border-radius: var(--radius-pill);
  background: var(--danger); color: #fff; font-size: var(--text-micro); font-weight: 800;
}

/* D2 status chips-with-counts. Each chip = label + its count; the active one
   fills accent. Tone modifiers tint the COUNT bubble by meaning. */

.stb-chips { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.stb-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 13px; border-radius: var(--radius-pill); cursor: pointer;
  background: var(--bg-card); border: 1px solid var(--border);
  color: var(--text-secondary); font-family: inherit; font-size: 12.5px; font-weight: 600;
  transition: border-color var(--state-duration, .15s) var(--state-ease, ease),
              background var(--state-duration, .15s) var(--state-ease, ease);
}

.stb-chip:hover { border-color: var(--border-strong); }

.stb-chip.is-active { background: var(--accent-soft); border-color: transparent; color: var(--accent-text); font-weight: 700; }

.stb-chip__count {
  display: inline-grid; place-items: center; min-width: 18px; height: 17px;
  padding: 0 6px; border-radius: var(--radius-pill); font-size: var(--text-xs); font-weight: 700;
  background: var(--bg-subtle); color: var(--text-tertiary);
}

.stb-chip.is-active .stb-chip__count { background: var(--accent-text); color: var(--bg-card); }

.stb-chip--success .stb-chip__count { background: var(--success-soft); color: var(--success); }

.stb-chip--warning .stb-chip__count { background: var(--warning-soft); color: var(--warning); }

.stb-chip--danger  .stb-chip__count { background: var(--danger-soft);  color: var(--danger);  }

.stb-chip--info    .stb-chip__count { background: var(--info-soft);    color: var(--info);    }

/* Premium removable active-filter chips bar (Linear/Airtable pattern). */

.stb-active { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.stb-active__label { font-size: var(--text-meta); color: var(--text-tertiary); }

.stb-active__chip {
  display: inline-flex; align-items: center; gap: var(--space-1-5);
  padding: 5px 6px 5px 11px; border-radius: var(--radius-pill);
  background: var(--bg-card); border: 1px solid var(--border-strong);
  color: var(--text-primary); font-size: var(--text-meta);
}

.stb-active__x { display: inline-flex; align-items: center; background: none; border: none; cursor: pointer; color: var(--text-tertiary); padding: 0; }

.stb-active__x:hover { color: var(--danger); }

.stb-active__clear { background: none; border: none; cursor: pointer; color: var(--accent-text); font-size: var(--text-meta); font-weight: 600; font-family: inherit; }

/* Selection morph — the tray becomes an accent bulk-action bar in place. */

.screen-toolbar--selecting {
  flex-direction: row; align-items: center; gap: var(--space-2-5); flex-wrap: wrap;
  padding: 10px 14px; border-radius: var(--radius);
  background: var(--accent-soft); border: 1px solid var(--accent);
}

.stb-sel-count { display: inline-flex; align-items: center; gap: var(--space-2); font-size: 13.5px; font-weight: 700; color: var(--accent-text); }

.screen-toolbar--selecting .stb-spacer { flex: 1; }

/* The screen's ONE primary action = the v2 "hero action" button (owner
   2026-07-09: match the mockup button exactly EXCEPT the color - keep the theme
   --accent so it flips with the theme). Taller, more rounded, an always-on
   accent-tinted lift. Scoped to the header so it upgrades EVERY screen's primary
   action (they all route through ScreenHeader) without bloating the dense/inline/
   modal .btn instances. */

.screen-header__actions .btn {
  height: 46px;
  padding: 0 22px;
  gap: 9px;
  border-radius: 13px;
  font-size: 14.5px;
  font-weight: 700;
  white-space: nowrap;
}

.screen-header__actions .btn.primary {
  box-shadow: 0 10px 22px -12px color-mix(in srgb, var(--accent) 50%, transparent);
}

.screen-header__actions .btn.primary:hover:not(:disabled) {
  box-shadow: 0 13px 26px -12px color-mix(in srgb, var(--accent) 58%, transparent);
}

/* ══════════════════════════════════════════════════════════════════
   W-HDR (owner 2026-07-17) — the header joins the app's ONE device line.
   ══════════════════════════════════════════════════════════════════
   Was `max-width: 640px` — an orphan value matching no token and no
   neighbour. The band that stacks on a phone flipped at THREE different
   widths: header 640 · tab bar 720 · topbar + .kpi-grid + bottom nav 768.
   Between 641-768 the shell was in mobile mode (KPI already 2-up, bottom
   nav on) while the header still ran its desktop row - a 128px dead zone
   where the CTA's place was decided by the title string (see the layout
   note above). 768px = --bp-tablet in tokens.css AND the app's de-facto
   device line, so the whole band now flips in ONE step.
   Sizes step with it: the 22px title + 44px chip + 46px action were
   authored for a 1440px canvas and never shrank - on a 375px phone the
   chrome ate 23% of the viewport. The action lands on 44px = the exact
   WCAG touch target .icon-btn already uses at this breakpoint (:10029),
   which also squares the header's gear-vs-CTA 2px mismatch.
   ══════════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {
  /* ADR-288 follow-up (owner 2026-07-25: "التاريخ ميخدش صف زيادة").
     ────────────────────────────────────────────────────────────────
     The one-column stack below was correct while the actions cell held a
     full-width primary BUTTON. Now that the button is docked in the bottom
     bar, the cell holds only the date pill — and stacking it cost a whole
     46px row (measured: the header was 98px on a 375 phone, 42 of it the
     title row and 46 a row containing one pill).
     The button vacated exactly the space the date needs, so the header goes
     back to its DESKTOP two-column shape on phones: title on the start edge,
     date pill shrink-to-fit on the end edge, one row. 98px -> 42px. */
  /* ── W-HDR-CAP (owner screenshot 2026-08-13, marketing) ──────────────────
     The action column was `auto`, i.e. sized to its own content with no
     ceiling. Marketing puts a month picker (then capped at 50vw = 206px) AND
     a settings gear in the date slot, so on a 412px phone the column claimed
     ~260px and left ~50px for the icon + title + description: the title
     ellipsized to "الـتـ…" and the description wrapped one character per line.
     `minmax(0, 52%)` makes the CONTROLS shrink first and guarantees the
     screen's identity just under half the row. This is the central law for
     every screen — appointments passes a doctor <select> + the same picker
     into that slot and was one long doctor name away from the same collapse.
     Desktop is untouched: it keeps the uncapped `auto` from the base rule. */
  .screen-header {
    /* 58%, not 52%: the owner kept the month steppers (see the date-control
       block below), so the controls need a little more of the row. The lead
       still keeps a guaranteed 42% — it had ~13% before this cap existed. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 58%);
    align-items: center;
    gap: var(--space-2-5);
  }
  /* The shrink has to be able to travel: a flex/grid item's default
     `min-width: auto` floors it at its content and would push the overflow
     back out of the capped track. */
  .screen-header__actions,
  .screen-header__date,
  .screen-header__date > * { min-width: 0; }
  /* ── and the control inside it has to be ABLE to shrink ──────────────────
     A cap loses to a floor. `.date-control--month .date-control__label` sets
     min-width: 132px, and the pill is flanked by two 38px steppers, so the
     month control is 224px that no track can compress; with the settings gear
     that is 272px of a 380px row, which is how the marketing lead ended up
     with ~14px and the description wrapped one character per line.
     The first attempt bought the room by dropping the two stepper arrows on
     phones. The owner rejected that (2026-08-13): stepping month to month is
     the actual gesture on the appointments calendar, and the native month
     wheel is a worse trade for it than it looked on paper. So the arrows STAY
     and the width comes from everything else instead:
       - the arrows shrink 38 -> 32 (still above the 32px touch floor)
       - the pill loses its 132px floor and its in-pill calendar glyph; with
         two arrows either side, the glyph is the third thing on the row
         saying "date"
       - the month text may ellipsize, as the last resort rather than the first
     That is ~176px instead of 224px, and it cannot punch out of the track.
     Scoped to the header slot — a DateControl in a page body is untouched. */
  .screen-header__date .date-control { min-width: 0; gap: var(--space-1); }
  .screen-header__date .date-control--month > .icon-btn {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
  }
  .screen-header__date .date-control--month .date-control__label > svg { display: none; }
  .screen-header__date .date-control--month .date-control__label {
    min-width: 0;
    height: 40px;
    padding: 0 10px;
  }
  .screen-header__date .date-control__month-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .screen-header__lead { align-items: center; min-width: 0; }
  .screen-header__actions { width: auto; flex-wrap: nowrap; justify-content: flex-end; }
  /* The primary stretches; an icon-btn (a secondary, e.g. the pricing
     gear) keeps its square touch target - it is not a .btn. */
  .screen-header__actions .btn { flex: 1 1 auto; }
  .screen-header__actions .btn { height: 44px; padding: 0 18px; font-size: var(--text-base); }
  /* ADR-288: the ONE primary action LEAVES the page on phones — it is docked
     in the centre of the bottom bar (MobileActionFab), where a thumb reaches
     it and no scroll can take it away. Only the direct-child primary goes:
     the date slot and any `.icon-btn` secondary stay put, because those are
     contextual controls, not the screen's one action. `display:none` (not
     `visibility`) so the actions row collapses to nothing on the screens
     where the action was its only child. The button is still RENDERED — it
     stays the desktop's renderer and the channel's source of truth. */
  .screen-header__actions > .btn { display: none; }
  /* Same rule for the legacy channel-only bar, so a screen that ever declares
     an action WITHOUT a header can't reintroduce a second on-page CTA. */
  .screen-action-bar { display: none; }

  /* The date pill sheds what the calendar glyph already says. "الفترة
     الزمنية" over the value is a two-line stack on a surface where the icon
     IS the label — so on phones the caption goes and the pill collapses to
     one line: [icon] value [chevron]. It also stops being allowed to bully
     the title: it may take at most 55% of the row and truncates before the
     screen name does. */
  .screen-header__date .date-range-btn__label { display: none; }
  .screen-header__date .date-range-btn {
    min-height: 40px;
    padding: 6px 10px;
    gap: 7px;
    /* W-HDR-CAP: the VIEWPORT is no longer the constraint — the capped action
       track is. 50vw ignored the gear sitting next to the picker, so the pair
       still overflowed the half-row this pill was trying to respect. */
    max-width: 100%;
  }
  /* A long custom range ("11 نوفمبر 2025 - 25 أغسطس 2026") makes the auto
     column wide enough to squeeze the title column until the TITLE wraps to a
     second line — which put the extra row back by another route. The title
     truncates instead: the screen name is also in the topbar right above, so an
     ellipsis here costs nothing, while a wrapped title costs 40px. */
  .screen-header__title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .screen-header__date .date-range-btn__value { font-size: var(--text-sm); }
  .screen-header__date .date-range-trigger { max-width: 100%; }
  .screen-header__title { font-size: 18px; letter-spacing: -0.2px; }
  /* Prose may be cut; state may not. The two stack so the description gets the
     same one-line ellipsis contract the title above it already has, while the
     chips keep their own wrapping row and stay readable whole. */
  .screen-header__subrow {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1-5);
  }
  .screen-header__sub {
    font-size: 12.5px;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The inline separator belongs to the one-line desktop form only. */
  .screen-header__sub + .screen-header__status::before { content: none; }
  .id-chip, .screen-header__icon { --chip-size: 36px; --chip-radius: 11px; }
  .screen-header__lead--identity { gap: var(--space-2-5); }
}

.tab.is-disabled,
.tab[aria-disabled="true"],
.tab:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ════════════════════════════════════════════════════════════════════
   U-2 PRIMITIVE SUPPLEMENT — Tooltip
   ════════════════════════════════════════════════════════════════════
   Styles for app/src/ui/tooltip.jsx. The tooltip portal-renders into
   <body> so this class doesn't need to coexist with screen layouts.
   Uses tokens for z-index (--z-tooltip), type (--text-xs), radius,
   and motion easing (--ease-out). High-contrast colour-inverts on
   dark themes.
*/

.ui-tooltip {
  z-index: var(--z-tooltip);
  max-width: 280px;
  padding: 7px 11px;
  border-radius: var(--radius-sm);
  background: rgba(15, 23, 42, 0.92);
  color: #fff;
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: var(--leading-snug);
  letter-spacing: 0;
  pointer-events: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.22), 0 2px 4px rgba(0, 0, 0, 0.12);
  animation: tooltipIn 160ms var(--ease-out) both;
}

[data-theme="luxe"] .ui-tooltip,
[data-theme="midnight"] .ui-tooltip,
[data-theme-mode="dark"] .ui-tooltip {
  background: rgba(241, 245, 251, 0.96);
  color: #0b1020;
}

@keyframes tooltipIn       { from { opacity: 0; transform: translateY(2px);  } to { opacity: 1; transform: translateY(0); } }

@keyframes tooltipInBottom { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: translateY(0); } }

@keyframes tooltipInLeft   { from { opacity: 0; transform: translateX(2px);  } to { opacity: 1; transform: translateX(0); } }

@keyframes tooltipInRight  { from { opacity: 0; transform: translateX(-2px); } to { opacity: 1; transform: translateX(0); } }

.ui-tooltip--bottom { animation-name: tooltipInBottom; }

.ui-tooltip--left   { animation-name: tooltipInLeft;   }

.ui-tooltip--right  { animation-name: tooltipInRight;  }

@media (prefers-reduced-motion: reduce) {
  .ui-tooltip { animation: none; }
}

/* ════════════════════════════════════════════════════════════════════
   U-2 PRIMITIVE SUPPLEMENT — EmptyState
   ════════════════════════════════════════════════════════════════════
   Tone + size variants for app/src/ui/empty-state.jsx. The base
   .empty-state.v2 styles (defined earlier in SPRINT 1) are not
   touched — this block only adds the new combinator classes so any
   existing consumer of .empty-state.v2 keeps rendering identically.
*/

.empty-state.v2.tone-danger  .empty-state-icon { background: var(--danger-soft);  color: var(--danger);  }

.empty-state.v2.tone-success .empty-state-icon { background: var(--success-soft); color: var(--success); }

.empty-state.v2.tone-warning .empty-state-icon { background: var(--warning-soft); color: var(--warning); }

.empty-state.v2.tone-info    .empty-state-icon { background: var(--info-soft);    color: var(--info);    }

.empty-state.v2.is-lg {
  padding: 56px 28px;
  gap: var(--space-4);
}

.empty-state.v2.is-lg .empty-state-icon {
  width: 80px;
  height: 80px;
  border-radius: 22px;
}

.empty-state.v2.is-lg .empty-state-title { font-size: var(--text-title); }

.empty-state.v2.is-lg .empty-state-text  { font-size: var(--text-md); max-width: 420px; line-height: var(--leading-relaxed); }

/* ════════════════════════════════════════════════════════════════════
   U-2 PRIMITIVE SUPPLEMENT — MetricTile
   ════════════════════════════════════════════════════════════════════
   Adds the .is-readonly modifier for non-interactive tiles rendered
   by app/src/ui/metric-tile.jsx. Base .metric-tile styles (SPRINT 1,
   line ~7050) remain canonical — this rule just removes the pointer
   cursor when the JSX wrapper isn't given an onClick handler.
*/

.metric-tile.is-readonly {
  cursor: default;
}

/* V-Appointments owner polish: subtle hover for readonly tiles. The
   original rule killed all hover effects on is-readonly to prevent
   misreading a non-clickable tile as a button. But that left
   Reception's 5 KPI tiles feeling dead next to the dashboard cards
   (which hover). Keep the shape of the interactive hover but soften
   it — half the lift, lighter shadow,
   muted border tint — so the tile reads as "alive, info display"
   rather than "alive, click me". The cursor stays default (no
   pointer affordance), so the visual cue maps to "this is data, not
   an action." */

@media (hover: hover) {
.metric-tile.is-readonly:hover {
  transform: translateY(var(--lift-1));
  box-shadow: 0 4px 12px color-mix(in srgb, var(--tile-color, var(--accent)) 6%, transparent);
  border-color: color-mix(in srgb, var(--tile-color, var(--accent)) 20%, var(--border));
}
}

.metric-tile[role="button"]:focus-visible {
  outline: none;
  box-shadow: var(--ring-shadow);
}

/* ════════════════════════════════════════════════════════════════════
   U-2 PRIMITIVE SUPPLEMENT — DataTable
   ════════════════════════════════════════════════════════════════════
   Adds the striped / compact / no-sticky modifiers + the empty-wrap
   container for app/src/ui/data-table.jsx. Base .data-table styles
   (defined at line ~1067 and the Phase 4 polish at line ~5109) are
   not touched.
*/

.data-table.is-striped tbody tr:nth-child(even) td {
  background: color-mix(in srgb, var(--bg-subtle) 55%, transparent);
}

.data-table.is-compact thead th { padding: 8px 12px;  font-size: var(--text-xs); }

.data-table.is-compact tbody td { padding: 9px 12px;  font-size: var(--text-sm); }

.data-table.no-sticky thead th { position: static; }

.data-table tbody tr[role="button"]:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}

.data-table-empty-wrap {
  display: grid;
  place-items: center;
  min-height: 200px;
  padding: var(--space-6);
}

/* U-4 DataTable page-number pager CSS deleted 2026-07-24: paging is ONE
   control app-wide now - the shared .load-more-footer button. */

/* Breadcrumbs (U-7/1) CSS deleted W2-C3 — the breadcrumbs system was removed
   (breadcrumbs.jsx + nav-utils.js; never rendered, ScreenTabBar replaced it).
   .doctor-scope-banner (below, live) was interleaved here and is kept. */

/* Member Identity W3 (ADR-199): scoped-doctor context strip. Sits below the
   topbar/breadcrumbs; explains why the doctor's counts are smaller than the
   clinic total. Subtle info tone, full-width, not dismissible (it is a
   persistent context, not a one-off notice). */

.doctor-scope-banner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 8px 24px;
  background: var(--info-soft, var(--accent-soft));
  color: var(--info, var(--accent));
  border-bottom: 1px solid var(--border);
  font-size: var(--text-xs, 12px);
  font-weight: 600;
  flex-shrink: 0;
}

.doctor-scope-banner__icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

/* .breadcrumbs-list / -item / -sep / -link / -current + their media queries
   deleted W2-C3 with the breadcrumbs system. */

/* ───────────────────────────────────────────────────────────────────
   Mobile bottom-sheet navigation (U-7/2)
   Hamburger button lives in the Topbar; tapping it calls the
   window.openMobileNav() opener exposed by app/shell/mobile-nav.jsx,
   which portals a backdrop + sheet hosting the existing <Sidebar />.
   The desktop sidebar is hidden at ≤768px by the @media block above —
   this section only adds the new mobile surfaces.
   ──────────────────────────────────────────────────────────────── */

.topbar-hamburger {
  display: none; /* desktop default — only the @media block reveals it */
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-elevated);
  color: var(--text-primary);
  cursor: pointer;
  font: inherit;
  transition: background var(--duration-fast) var(--ease-smooth), border-color var(--duration-fast) var(--ease-smooth);
}

.topbar-hamburger:hover {
  background: var(--bg-hover);
  border-color: var(--accent);
}

.topbar-hamburger:focus-visible {
  outline: none;
  box-shadow: var(--ring-shadow-soft);
  border-color: var(--accent);
}

/* V-Mobile/BackStack: the topbar back-arrow button was removed (2026-07-05).
   Its markup is gone from topbar.jsx (system back + hamburger cover backward
   navigation on mobile; desktop never showed it), so all its styles were
   deleted with it. */

.topbar-hamburger-bars {
  position: relative;
  display: block;
  width: 18px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}

.topbar-hamburger-bars::before,
.topbar-hamburger-bars::after {
  content: '';
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}

.topbar-hamburger-bars::before { top: -6px; }

.topbar-hamburger-bars::after  { top:  6px; }

.mobile-nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  z-index: var(--z-modal-backdrop, 1000);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  align-items: flex-end;
  justify-content: stretch;
  animation: backdropIn 220ms ease-out;
}

.mobile-nav-backdrop.is-exiting {
  animation: mobileNavBackdropFadeOut 200ms ease-out forwards;
}

[data-theme="luxe"] .mobile-nav-backdrop,
[data-theme="midnight"] .mobile-nav-backdrop,
[data-theme-mode="dark"] .mobile-nav-backdrop {
  background: rgba(0, 0, 0, 0.6);
}

/* The command palette used to share this block via a second selector. It now
   rides the central ResponsiveModal instead, whose `.rm-card` is already the
   same bottom sheet - so the sharing moved up a level, from "two selectors on
   one block" to "one shell". This block is the sidebar sheet's alone again. */

.mobile-nav-sheet {
  width: 100%;
  max-height: 92dvh;
  min-height: 320px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  color: var(--text-primary);
  border: 1px solid var(--glass-border);
  border-bottom: 0;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  box-shadow: var(--glass-shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: mobileNavSheetSlideUp 320ms cubic-bezier(0.34, 1.32, 0.64, 1);
}

.mobile-nav-backdrop.is-exiting .mobile-nav-sheet {
  animation: mobileNavSheetSlideDown 200ms ease-in forwards;
}

.mobile-nav-chrome {
  position: relative;
  flex-shrink: 0;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
  background: color-mix(in srgb, var(--bg-subtle) 55%, transparent);
}

.mobile-nav-handle {
  width: 44px;
  height: 4px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--text-tertiary) 38%, transparent);
}

.mobile-nav-close {
  position: absolute;
  inset-inline-end: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 78%, transparent);
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-smooth),
    color var(--duration-fast) var(--ease-smooth),
    transform var(--duration-fast) var(--ease-smooth);
}

.mobile-nav-close:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}

.mobile-nav-close:active {
  transform: translateY(-50%) scale(0.96);
}

.mobile-nav-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: env(safe-area-inset-bottom, 0);
  /* See .rm-body: keep an at-top pull-down inside the sheet for swipe-to-dismiss. */
  overscroll-behavior: contain;
}

/* Sidebar rendered inside the sheet: full width, no rail border,
   no overflow clipping — the .mobile-nav-body handles scrolling. */

.mobile-nav-sheet .sidebar {
  width: 100%;
  height: auto;
  border: 0;
  background: transparent;
  overflow: visible;
  flex-shrink: 0;
}

.mobile-nav-sheet .sidebar-navigation,
.mobile-nav-sheet .sidebar-navigation-group {
  flex: none;
  min-height: auto;
}

.mobile-nav-sheet .sidebar-navigation-group > .nav-items {
  flex: none;
  justify-content: flex-start;
}

/* V-Phase2 Wave 2 hotfix: the desktop .logo-wrap caps height at 88px
   with overflow:hidden plus the 2px upward translate calibrated for
   the desktop rail gutter, but the size=lg logo (220px wide) has a
   natural height greater than 88px so the top of the wordmark (the
   "M" spire and the "i" dot) gets clipped inside the mobile
   bottom-sheet where there's plenty of vertical room. Reset the cap
   plus the transform here, let the image render at its natural
   aspect ratio, and center it horizontally so it reads as a brand
   header for the sheet. */

.mobile-nav-sheet .sidebar .sidebar-logo {
  justify-content: center;
  padding: 12px 16px 14px;
}

/* ============================================================
   V-Phase2 Wave 3a — Date range popover mobile (ADR-132)
   ============================================================
   Owner audit on phone reported the date-range filter popover
   extending past the trailing edge of the viewport, clipping
   the preset chips ("الشهر السابق" => "...بر السابق", "السنة
   الحالية" => "...نة الحالية") and the "مسح الفترة" action.
   Root cause: the popover anchors to `left: 0` of its
   .date-range-trigger wrapper, which inside the .l-cluster
   controls row can sit anywhere horizontally on a phone after
   wrap. Once the trigger sits in the right half of the row,
   the popover (311px wide on 375px viewport) extends past
   the inline-end edge.

   Fix (2026-07-06, BUG-A): a fixed/absolute popover mis-positions inside the
   mobile shell's TRANSFORMED route container — a transform ancestor re-anchors
   position:fixed to ITSELF, not the viewport, so the owner saw the picker
   "popping in the wrong place" (بيطلع في مكان غير مكانه). At <=768px render the
   popover IN NORMAL FLOW (position:static) below the trigger: immune to any
   transformed ancestor + never clipped. The native <input type=date> wheel is a
   browser overlay, so it renders above everything regardless. */

@media (max-width: 768px) {
  .date-range-trigger {
    flex-direction: column !important;
    align-items: stretch !important;
  }
  .date-range-trigger > .date-range-popover {
    position: static !important;
    inset: auto !important;
    width: 100% !important;
    max-width: none !important;
    margin-top: 8px !important;
    max-height: none !important;
    overflow: visible !important;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.10) !important;
  }
  /* The popover renders in normal flow, so its width is its TRIGGER's width -
     and in the header the trigger is one flex item sharing the row with the
     primary action, i.e. about half a phone screen. A month calendar cannot
     live in ~190px. While the popover is open, the date slot claims the whole
     header row (the actions row already wraps), so the calendar gets the full
     width and the trigger returns to sharing the row on close.
     position:fixed is deliberately NOT used here - see BUG-A above: a
     transformed route ancestor re-anchors fixed to itself. */
  .screen-header__date:has(.date-range-trigger.is-open) {
    flex: 1 0 100%;
  }
}

/* Anatomy W1-F6-b5: range-calendar day cells. Classes (not inline colors) so
   THEMES override the on-accent text exactly like .btn.primary — obsidian +
   indigo ship a LIGHT accent where hardcoded white was unreadable (the same
   family as the stuck-white popover the owner caught). */

.drf-day {
  min-height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-primary);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}

.drf-day:hover { background: var(--bg-subtle); }

.drf-day--in, .drf-day--in:hover { background: var(--accent-soft); color: var(--accent-text); }

.drf-day--edge, .drf-day--edge:hover { background: var(--accent); color: #fff; }

.drf-day--today:not(.drf-day--edge) { border-color: var(--accent); }

[data-theme="obsidian"] .drf-day--edge { color: #14110a; }

[data-theme="indigo"] .drf-day--edge { color: #0c0f25; }

[data-theme="petrol"] .drf-day--edge { color: #04222a; }

/* ADR-141: on desktop LTR the dashboard date filters sit at the right edge of
   the header, so the popover's inline `left:0` anchor overflows; right-anchor it
   in LTR so it opens inward. Desktop only now (<=768px = inline flow above). */

@media (min-width: 769px) {
  [dir="ltr"] .date-range-trigger > .date-range-popover {
    left: auto !important;
    right: 0 !important;
  }
}

/* ============================================================
   V-Phase2 Wave 3b — Patient File mobile polish (ADR-132)
   ============================================================
   Owner audit reported on the patient-file screen:
     - 9 inner tabs with no visible scroll affordance
     - The PatientHeader's 7 actions (4 icons + 3 text buttons +
       1 primary CTA) stack one-per-row on phones, eating ~150px
       of chrome
     - الفواتير + الجلسات render their DataTables as raw tables,
       so 7-8 columns get squeezed to ~50px each on a 375px
       viewport and the actions column is clipped
   Wave 3b fixes:
     A. PatientHeader actions compressed for phones
     B. .pf-tab-strip edge gradient hint that scroll is possible
     C. mobileCard enabled on Invoices + Sessions (JSX-side)
*/

@media (max-width: 480px) {
  /* A — PatientHeader actions: pack icons into a tight row, drop
     the "السجل الطبى" + "اشرح للمريض" labels to icon-only so the
     primary "حجز موعد جديد" CTA gets its own full-width row at
     the end. flex-wrap stays on so the icons stay readable when
     the row gets narrow.

     Text-hiding strategy: font-size: 0 on the button collapses
     the bare text-node children (the labels are written as direct
     text nodes after the SVG, not wrapped in spans). The lucide
     SVG icons set width + height via attributes (the size prop),
     so font-size: 0 doesn't shrink the glyph — it stays at 16px.
     title + aria-label on each button keep the long form available
     for screen readers and hover tooltips. */
  /* width: auto overrides the existing
     `.dash-hero-actions .btn { width: 100%; }` at the same media
     query (line ~10597). Without this the icon-only-on-mobile
     buttons would still stretch to the full row width. */
  /* The primary CTA "حجز موعد جديد" takes the whole row so it
     reads as the dominant action. */

  /* B — Patient File tab strip edge gradient hint. The 9 tabs
     overflow to horizontal scroll on phones; without a visual
     fade at the trailing edge users don't realize they can swipe.
     The gradient + an inline 12px scroll-padding-inline reveal
     hidden tabs slightly so the affordance is obvious. */
        }

@media (max-width: 768px) {
  /* 1 — Overview activity + quick actions stack vertically */
  
  /* 2 — Manual send: three sections stack as a wizard. Slightly
     larger gap so the visual rhythm of step-by-step is clear. */
  
  /* 3 — Inbox master-detail. When a thread is selected the list
     collapses + the chat goes full-width. When nothing is
     selected the chat panel doesn't render (the JSX already
     skips it when !selectedThread), so the list takes the
     whole screen by default — no extra rule needed. */
  .wa-inbox-grid--has-selected .wa-inbox-list {
    display: none;
  }
  .wa-inbox-grid--has-selected .wa-inbox-thread {
    height: calc(100dvh - 220px);
    min-height: 420px;
  }
}

/* ============================================================
   V-Phase2 Wave 3b-extras — Dental chart + X-ray lightbox mobile
   ============================================================
   Owner audit on the Patient File flagged two remaining mobile
   issues that didn't fit in Wave 3b:

   1. Dental chart at 375px:
      - The outer 1fr+320px split forced the chart pane into ~23px
        of content width while the side detail panel ate 320px.
      - Tooth row at repeat(16, 1fr) gave ~21px per tooth — well
        below the 44px touch-target floor; doctors mis-tapped
        adjacent teeth constantly.
      Fix: at <=768px, collapse the shell to a single column so
      the chart gets the full width; switch the tooth row to a
      flex layout with min-content per tooth (36px) + horizontal
      scroll. The doctor swipes the row to reach distant teeth
      rather than hunting tiny targets.

   2. MediaLightbox at 375px:
      - The central ResponsiveModal composition keeps viewer chrome inside a
        viewport-height mobile sheet and preserves native pinch zoom.
*/

/* X-ray thumbnail hover: a dark scrim + a preview (eye) icon revealed on
   hover. Hover Wave 1 - was a hand-rolled JS background swap that set the
   scrim but NEVER revealed the eye (its opacity stayed 0 - a latent bug
   since the gallery shipped). Pure CSS parent-hover reveal shows both. */

.xray-thumb__scrim {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0);
  transition: background 0.2s;
}

.xray-thumb:hover .xray-thumb__scrim {
  background: rgba(0, 0, 0, 0.3);
}

.xray-thumb__eye {
  opacity: 0;
  transition: opacity 0.2s;
}

.xray-thumb:hover .xray-thumb__eye {
  opacity: 1;
}

/* Central MediaLightbox — ResponsiveModal owns the sheet/backdrop lifecycle. */

.rm-card.media-lightbox {
  height: min(88dvh, 760px);
}

.rm-card.media-lightbox .rm-body {
  padding: 0;
  overflow: hidden;
}

.media-lightbox__layout {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  background: #0b1020;
}

.media-lightbox__stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: repeating-linear-gradient(45deg, #0b1020, #0b1020 14px, #111a2e 14px, #111a2e 28px);
}

.media-lightbox__image {
  display: block;
  max-inline-size: 100%;
  max-block-size: 100%;
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  transform-origin: center;
  transition: transform 180ms ease;
  touch-action: pinch-zoom;
}

.media-lightbox__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-6);
  color: rgba(255, 255, 255, 0.72);
  text-align: center;
}

.media-lightbox__navigation {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.media-lightbox__nav {
  position: absolute;
  inset-block-start: 50%;
  transform: translateY(-50%);
  inline-size: 48px;
  block-size: 48px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: var(--radius-pill);
  background: rgba(3, 7, 18, 0.72);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  pointer-events: auto;
}

.media-lightbox__nav--previous { inset-inline-start: 12px; }

.media-lightbox__nav--next { inset-inline-end: 12px; }

.media-lightbox__nav:disabled {
  opacity: 0.3;
  cursor: default;
}

.media-lightbox__meta {
  flex: 0 0 auto;
  padding: 8px 16px 0;
  color: rgba(255, 255, 255, 0.7);
  font-size: var(--text-meta);
  text-align: center;
}

.media-lightbox__toolbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2-5);
  padding: 12px 16px 16px;
}

.media-lightbox__zoom,
.media-lightbox__actions {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
}

.media-lightbox__control,
.media-lightbox__zoom-reset {
  min-inline-size: 44px;
  min-block-size: 44px;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
}

.media-lightbox__zoom-reset {
  min-inline-size: 68px;
  font-weight: 800;
}

.media-lightbox__control:disabled {
  opacity: 0.35;
  cursor: default;
}

.media-lightbox__actions .btn {
  min-block-size: 44px;
}

@media (max-width: 768px) {
  /* 1a — Stack the chart + detail panel vertically so the chart
     gets the full content width. The detail panel (selected
     tooth info + state assignment) drops below. */
  .dc-shell {
    grid-template-columns: 1fr !important;
    gap: 12px !important;
  }
  /* 1b — Tooth row: min-content flex with horizontal scroll.
     Each tooth gets 36px min-width which keeps the SVG legible
     and the touch target safe; gap shrinks slightly to fit a
     few more teeth on-screen per scroll position. !important
     needed because the inline grid-template-columns sets the
     equal-fraction layout at the same specificity level. */
  .dc-tooth-row {
    display: flex !important;
    grid-template-columns: none !important;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 8px;
    gap: 4px !important;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
  }
  .dc-tooth-row > * {
    flex: 0 0 36px;
    min-width: 36px;
    scroll-snap-align: center;
  }
  /* Surround the tooth rows in their parent .dc-chart-surface so
     the upper-jaw + lower-jaw labels still center over the
     scrollable strips and a subtle scroll affordance hints that
     the row continues beyond the viewport. */
  .dc-chart-surface {
    position: relative;
  }

  /* 2 — The central viewer becomes a viewport-height bottom sheet. */
  .rm-card.media-lightbox {
    height: 94dvh;
    max-height: 94dvh;
  }
  .media-lightbox__stage {
    min-height: calc(100dvh - 260px);
  }
  .media-lightbox__toolbar,
  .media-lightbox__actions {
    inline-size: 100%;
  }
  .media-lightbox__actions .btn {
    flex: 1 1 auto;
  }
}

/* ADR-170 Fix #5 (2026-06-05): tighter floor for phone-class viewports.
   The Wave 3b-extras 36px tooth size at <=768px keeps 16 teeth visible
   on a 7-inch tablet, but at <=600px (phones + 6-inch tablets) doctors
   were still mis-tapping. Per WCAG 2.1 the touch-target floor is 44px;
   this block raises every tooth to that floor at small viewports and
   widens the gap slightly so adjacent teeth never share an edge. The
   horizontal scroll the parent block already enables continues to let
   the doctor reach distant teeth. */

@media (max-width: 600px) {
  .dc-tooth-row > * {
    flex: 0 0 44px;
    min-width: 44px;
    min-height: 44px;
  }
  .dc-tooth-row { gap: 6px !important; }
}

/* ─────────────────────────────────────────────────────────────────────
   V-Phase2 Wave 4 (ADR-133) — TopBar + Billing mobile polish
   ─────────────────────────────────────────────────────────────────────
   Wave 4 closes three live overflow bugs the owner reported on
   2026-05-27:

   1. TopBar title clipped behind the search icon.
      Cause: `.cmdk-launch` was the first child of `.topbar-actions`
      (visually right-most in RTL, directly adjacent to the title
      block). The icon plus the chevron-shaped focus ring left the
      title (`ملف المريض`, `الفواتير والمدفوعات`, etc.) with no room
      to render its tail letters, so it always wrapped or clipped
      to a 2-letter stub.
      Fix: push `.cmdk-launch` to `order: 99` so it visually swaps
      to the LEFT end of `.topbar-actions` (where the avatar used
      to sit). Hide the avatar + its preceding divider on mobile —
      the user/clinic identity is already surfaced in the mobile
      nav sheet that the hamburger opens, so the topbar real estate
      is reclaimed entirely for navigation + key actions.

   2. DentalChart still overflowed at ≤768px after Wave 3b-extras.
      Cause: the previous `grid-template-columns: 1fr !important`
      didn't actually stop the track from expanding — `1fr` in CSS
      Grid resolves to `max(min-content, available-space)`, and the
      tooth row's intrinsic min-content (16 teeth × 36px + gaps =
      ~636px) bled UP through the chart card and forced the 1fr
      track to expand to 700px+, overflowing the 375px viewport.
      Fix: use `minmax(0, 1fr)` which clamps the track's minimum
      to 0, AND add `min-width: 0` + `overflow: hidden` to the two
      child cards so their internal layouts stop dictating the
      outer track size. Equivalent change applied to the New /
      Edit Invoice modal 2fr+1fr grids for the same reason — their
      5-column items tables were similarly bleeding intrinsic width
      into the outer grid.

   3. Billing KPI tiles felt oversized on phones.
      Cause: V-Mobile/KpiTypo's clamp (22→36px) does scale by
      viewport, but at 375px it still resolves to 22-24px which
      reads larger than the surrounding density. Padding + icon
      sizes were also still at desktop scale.
      Fix: tighten value to 20px, label to 10px, icon to 28px,
      padding to 12px on phones. Matches the tile chrome density
      already used on the inventory hub. */

@media (max-width: 768px) {
  /* (W4-A TopBar avatar/divider hide removed 2026-07-09 - the topbar avatar +
     divider were deleted entirely in F2, so the mobile hide rule is moot.) */

  /* W4-B — DentalChart: stop the 1fr column from expanding to fit
     the inner tooth row's min-content. minmax(0, ...) plus the
     child min-width: 0 are needed together — neither alone is
     enough because the inline grid-template-columns ships with
     `1fr 320px` (specificity tie that requires !important on the
     replacement). */
  .dc-shell {
    grid-template-columns: minmax(0, 1fr) !important;
  }
  .dc-chart-card,
  .dc-detail-card {
    min-width: 0;
    overflow: hidden;
  }

  /* W4-C — Billing KPI density: tighter padding + smaller value
     font so the four tiles read as compact metrics rather than
     hero panels. !important on the value font because the fluid
     clamp upstream (V-Mobile/KpiTypo) ships at the same
     specificity (a single class selector) and would otherwise
     win the cascade through source-order. */
  .metric-tile {
    padding: 12px 12px;
    gap: var(--space-1-5);
  }
  .metric-tile-value {
    font-size: 20px !important;
  }
  .metric-tile-icon {
    width: 28px;
    height: 28px;
    font-size: var(--text-base);
  }
  .metric-tile-label {
    font-size: var(--text-micro);
  }
  .metric-tile-hint {
    font-size: var(--text-micro);
  }
}

/* V-Phase2 Wave 4b (ADR-134) — Wave 4 D rules removed.
   The invoice modals no longer use a 2fr+1fr layout or 5-column
   items table — they now follow the unified vertical card-stack
   pattern shared by AddSessionModal + LabOrderModal +
   EditPurchaseInvoiceModal, so the @media (max-width: 720px) rules
   for `.new-invoice-2col`, `.new-invoice-items-table`,
   `.edit-invoice-items-table`, and `.ni-page-header--in-modal` are
   no longer needed and have been deleted. The items now wrap
   naturally via flex-wrap with min-widths per field. */

@keyframes mobileNavBackdropFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@keyframes mobileNavSheetSlideUp {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

@keyframes mobileNavSheetSlideDown {
  from { transform: translateY(0); }
  to   { transform: translateY(28px); opacity: 0; }
}

@media (max-width: 768px) {
  /* Reveal the hamburger and the bottom-sheet on phone-sized viewports.
     The sheet/backdrop only ever mount when window.openMobileNav() flips
     state in mobile-nav.jsx (returns null otherwise) — the display:flex
     here just unlocks the layout when they DO mount. */
  .topbar-hamburger { display: inline-flex; }
  /* Mobile Shell Wave 1: when the bottom bar is present its "المزيد" icon owns
     the sheet, so the now-redundant topbar hamburger is hidden. Gated on
     `.has-bottom-nav` (set by mobile-bottom-nav.jsx) so the kill-switch flag
     fully reverts — shell off => class absent => hamburger returns here. */
  .has-bottom-nav .topbar-hamburger { display: none; }
  .mobile-nav-backdrop { display: flex; }
}

/* Respect prefers-reduced-motion — skip the slide-up + fade-in for users
   with vestibular conditions or motion sensitivity. The sheet still
   appears (essential), just without the entrance animation. */

@media (prefers-reduced-motion: reduce) {
  .mobile-nav-backdrop,
  .mobile-nav-sheet {
    animation: none;
  }
}

/* =====================================================================
   V-Inventory (ADR-058) — eighth Wave 2 polish track
   ===================================================================== */

/* Inventory onboarding composes the central EmptyState primitive. */

.inventory-table-empty{ padding: var(--space-6); }

.inventory-empty-actions{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* ─── @media print — clean Analytics print output ────────────────────
   ADR-140 W3.3 (2026-05-28): "طباعة" button calls window.print().
   Before this block the printed page included sidebar + topbar +
   tabs + buttons + boot splash, wasting paper and producing
   unreadable charts. This hides chrome, flattens the colour
   palette, and lets cards / tables fit the page width. */

@media print {
  /* hide chrome */
  .sidebar,
  aside.sidebar,
  .topbar,
  .boot-splash,
  .modal-backdrop,
  [role="dialog"],
  .toast,
  .toast-stack,
  nav,
  [role="tablist"] {
    display: none !important;
  }
  /* expand main to full page */
  body, html, #root, main {
    background: #fff !important;
    color: #111 !important;
    margin: 0 !important;
    padding: 0 !important;
    min-height: 0 !important;
  }
  main {
    padding: 8px 12px !important;
    margin-inline-start: 0 !important;
  }
  /* flatten card backgrounds for print */
  .card,
  .metric-tile {
    background: #fff !important;
    box-shadow: none !important;
    border: 1px solid #d4d4d8 !important;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  /* hide action buttons (طباعة + نسخ + الاستيراد + similar) */
  button {
    display: none !important;
  }
  /* keep hero subtitle (date range + summary) — primary value */
  .breadcrumb {
    display: none !important;
  }
  /* tables keep readable */
  table, .data-table {
    page-break-inside: auto;
  }
  tr {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  /* keep currency Black */
  * {
    color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }
  /* SVG charts: keep colours but cap size */
  svg {
    max-width: 100% !important;
  }
}

/* ─── .var-* family — Variance tab responsive primitives ─────────────
   ADR-140 W4a (2026-05-28): the variance tab on phones had three
   independent layout failures:
   1. Header card 3-col grid wrapped subtitle to 6+ short lines
   2. Wizard 4-step flex row crammed 40px circles + 12px labels into
      ~80px cells with vertical dividers — looked like icon-soup
   3. Form rows (actual/physical/close) had 2/3-col grids that crushed
      the help text and shrunk the save button below thumb-tap width
   This block defines per-section primitives so the JSX uses semantic
   classNames + the responsive behaviour lives in CSS. Breakpoint:
   ≤720px (small tablet + phones). */

/* .var-header-grid removed (Anatomy W-JRD 2026-07-16): the bespoke variance
   header card is retired - the month picker rides the ONE ScreenHeader
   date-slot and the readiness gauge became an actionable hint card in the
   analytics inventory tab. */

/* .var-wizard family removed (Anatomy W-JRD 2026-07-16, owner review):
   the step wizard is retired - the stocktake flow is one ledger table. */

/* .var-form-row family + .var-qty-input removed (Anatomy W-JRD v3,
   2026-07-17): the inline-entry spreadsheet + the اعتماد strip are retired -
   entry lives in StocktakeEntryModal (ResponsiveModal recipe) and the ONE CTA
   rides the ScreenHeader, so no variance-specific layout CSS remains. */

/* .var-close-card + .var-scroll-wrap removed (Anatomy W-JRD 2026-07-16):
   superseded again by v3 - the اعتماد strip died too; the ONE CTA rides the
   ScreenHeader and entry lives in StocktakeEntryModal. Every table is a
   DataTable (mobile cards - no raw horizontally-scrolled table left). */

/* .bom-split family removed (Anatomy W-SVC 2026-07-17): the مكونات الخدمة
   master-detail tab was retired — its jobs live in بطاقة الخدمة (detail
   contract) + services/bom-modal.jsx (reception contract), both on central
   primitives with no bespoke CSS. */

/* ─── .metric-tile.is-active — clicked filter highlight (V-Inventory/3)
   Hover Wave 3d (owner, 2026-07-11): the hard 1.5px outline read as a
   foreign system next to the tiles' soft language ("التحديد مختلف").
   ONE selected look now: a soft tile-color WASH + a clearer border +
   the soft glow - consistent with the tile hover discipline, and the
   same on every clickable-filter screen (inventory + patients wire the
   same class). */

.metric-tile.is-active {
  background: color-mix(in srgb, var(--tile-color, var(--accent)) 8%, var(--bg-card));
  border-color: color-mix(in srgb, var(--tile-color, var(--accent)) 45%, var(--border));
  box-shadow: 0 6px 16px color-mix(in srgb, var(--tile-color, var(--accent)) 12%, transparent);
}

/* ─── .wa-empty — V-WhatsApp/4 (ADR-059) rich empty-state shell ─────
   WhatsApp's legacy empty shell mirrors the established empty-state recipe so consumers in
   overview-tab + inbox-tab + manual-tab render with identical
   dimensions regardless of host card padding. */

.wa-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 32px 20px;
  border: 2px dashed var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  gap: var(--space-1-5);
}

.wa-empty-icon {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  display: inline-grid;
  place-items: center;
  font-size: 28px;
  background: color-mix(in srgb, var(--text-tertiary) 10%, transparent);
  margin-bottom: 6px;
}

.wa-empty-title { font-weight: 800; font-size: var(--text-md); color: var(--text-primary); }

.wa-empty-hint  { font-size: var(--text-meta); color: var(--text-secondary); max-width: 480px; line-height: 1.7; }

.wa-empty.is-accent  .wa-empty-icon { background: var(--accent-soft);  }

.wa-empty.is-success .wa-empty-icon { background: var(--success-soft); }

.wa-empty.is-warning .wa-empty-icon { background: var(--warning-soft); }

.wa-empty.is-danger  .wa-empty-icon { background: var(--danger-soft);  }

.wa-empty.is-info    .wa-empty-icon { background: var(--info-soft);    }

.wa-empty.is-muted   .wa-empty-icon { background: color-mix(in srgb, var(--text-tertiary) 10%, transparent); }

/* ─── .wa-schedule-bar — V-WhatsApp/3 (ADR-059) info strip ──────────
   Pure informational schedule summary in automation-tab. The compact
   "⚙ الإعدادات والمواعيد" CTA that used to anchor this bar moved
   into the hero's tab-aware actions row, so the bar is now a
   single-line summary without an embedded button. */

/* ─── Settings shell + sidebar nav polish — V-Settings/4 (ADR-060) ──
   Replaces the inline `gridTemplateColumns: '220px 1fr'` from the
   Settings() return. The nav card stays vertical (the 7-tab depth
   doesn't fit a horizontal Tabs primitive comfortably); active item
   gets a 3px accent borderInlineStart stripe + soft accent bg so
   the current tab is scannable without color noise. */

.settings-shell {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: var(--space-5);
  align-items: start;
}

.settings-nav {
  padding: var(--space-2-5);
  position: sticky;
  top: 20px;
}

.settings-nav-item {
  position: relative;
  border-inline-start: 3px solid transparent;
  transition: background var(--duration-fast) var(--ease-smooth),
              border-inline-start-color var(--duration-fast) var(--ease-smooth);
}

.settings-nav-item.active {
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
  border-inline-start-color: var(--accent);
  font-weight: 800;
}

.settings-nav-item.active .icon-box {
  color: var(--accent);
}

/* Wave 6: mobile-only horizontal Tabs strip for Settings. Default hidden
   on desktop+ where the vertical sidebar reads better; flipped on
   <=768px where the sidebar wastes ~350px of vertical space before the
   content. Sticky at top so the active-tab indicator stays visible
   while scrolling through the form. */

/* Settings mobile tab strip. The unified ScreenTabBar inside owns sticky +
   opaque bg + elevate-on-scroll + swipe (one architecture); this wrapper only
   toggles desktop↔mobile visibility. It sits ABOVE the hero (a direct child of
   the screen root) so the strip keeps the ScreenTabBar's own side padding. */

.settings-tabs-mobile {
  display: none;
}

@media (max-width: 768px) {
  .settings-shell { grid-template-columns: 1fr; }
  /* ADR-292: the rail is no longer hidden on phones - it IS the phone UI.
     Settings became a menu you drill into, so the SAME rows the desktop rail
     renders take the full width and gain a subtitle + chevron. One array, one
     row component, one order: the two surfaces cannot drift. */
  .settings-nav {
    display: block;
    position: static;
    padding: var(--space-1-5);
  }
  .settings-nav-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: 'icon label chev' 'icon sub chev';
    align-items: center;
    column-gap: var(--space-3);
    width: 100%;
    min-height: 56px;            /* comfortably past the 44px touch target */
    padding: var(--space-2-5) var(--space-2);
    text-align: start;
  }
  .settings-nav-item .icon-box { grid-area: icon; }
  .settings-nav-label { grid-area: label; font-weight: 700; font-size: var(--text-sm); }
  .settings-nav-sub {
    grid-area: sub;
    font-size: var(--text-meta);
    color: var(--text-tertiary);
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .settings-nav-chev { grid-area: chev; color: var(--text-tertiary); flex-shrink: 0; }
  /* Rows read as a list, not as ten separate cards. */
  .settings-nav-item + .settings-nav-item { border-top: 1px solid var(--border); }
  /* The strip is retired on phones (ADR-292) - the menu replaced it. The rule
     stays so a stale build or a re-added markup node can never paint both. */
  .settings-tabs-mobile { display: none; }
}

/* ─── Settings nav groups (owner 2026-07-28) ────────────────────────────────
   Nine flat rows is a wall on a phone; three named shelves is a menu. The
   group label is a quiet eyebrow, not a second heading — it orients without
   competing with the row labels. */

.settings-nav-group + .settings-nav-group {
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}

.settings-nav-group-label {
  padding: var(--space-1) var(--space-2-5) var(--space-1-5);
  font-size: var(--text-meta);
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--text-tertiary);
}

@media (max-width: 768px) {
  /* The rows already carry a hairline between them; a group boundary needs to
     read as a BIGGER gap, not a double line. */
  .settings-nav-group + .settings-nav-group {
    margin-top: var(--space-4);
    border-top: 0;
    padding-top: 0;
  }
  .settings-nav-group-label { padding-inline: var(--space-2); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Settings form vocabulary — the ONE field anatomy (owner 2026-07-28).
   Consumed by app/src/ui/settings-form.jsx. Before this, five hand-rolled
   card-and-label dialects lived inside one screen; sections now compose the
   app-wide SectionCard and every field is the same object.
   ══════════════════════════════════════════════════════════════════════════ */

.settings-body { min-width: 0; }

.set-form { min-width: 0; }

/* minmax(0, 1fr), never a bare 1fr: a bare 1fr is min-width:auto and a <select>
   holding a long localized country name pushes the whole page sideways. */

.set-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
}

.set-grid--1 { grid-template-columns: minmax(0, 1fr); }

/* Three short pickers that belong together (the waiting screen's theme /
   name-mode / language). At two columns the third dangled alone on its own
   row. Falls back to two columns before it collapses to one, so the boxes
   never get narrower than a localized label needs. */

.set-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 1100px) { .set-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.set-field--wide { grid-column: 1 / -1; }

@media (max-width: 768px) {
  .set-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
  /* Every button inside a settings form is thumb-sized on a phone, including
     the `sm` variants (preview / copy / edit), which render at 34px — a third
     under the target. Scoped to `.set-form` on purpose: `.btn.sm` is used in
     dense tables and row actions across the app where 44px would wreck the
     layout. Settings is not dense; it is a form you poke at with a thumb. */
  .set-form .btn { min-height: 44px; }
}

.set-field { display: grid; gap: var(--space-1-5); min-width: 0; align-content: start; }

.set-field__label {
  font-size: var(--text-meta);
  font-weight: 700;
  color: var(--text-secondary);
}

/* One caption slot per field. Settings copy is a caption, not a paragraph. */

.set-field__hint,
.set-field__error {
  font-size: var(--text-meta);
  line-height: 1.5;
  color: var(--text-tertiary);
}

.set-field__error { color: var(--danger); font-weight: 650; }

.set-field.is-error .input,
.set-field.is-error .select { border-color: var(--danger); }

.set-readonly {
  display: flex;
  align-items: center;
  background: var(--bg-subtle);
  color: var(--text-secondary);
}

/* Switch row: text on one side, the central Toggle on the other. */

.set-switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
  padding: var(--space-2-5) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-subtle);
  min-width: 0;
}

.set-switch.is-disabled { opacity: 0.6; }

.set-switch__body { min-width: 0; }

.set-switch__label {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  font-weight: 700;
  font-size: var(--text-sm);
}

.set-switch__hint {
  margin-top: 2px;
  font-size: var(--text-meta);
  line-height: 1.5;
  color: var(--text-tertiary);
}

/* HIT SLOP. The Toggle pill is 42x24 by design — a deliberate visual size, and
   24px is well under the 44px minimum touch target. The row READS large (66-84px
   tall), which makes the miss worse: the thumb aims at the row and lands on
   nothing. This grows the tappable area to 44px without moving a pixel of the
   pill, so the switch looks identical and stops being a thumb-miss. Applied via
   the ARIA role, so every switch in settings inherits it rather than each row
   remembering. */

.set-switch [role="switch"]::after {
  content: '';
  position: absolute;
  inset: -10px -8px;
}

.set-inline { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* The one muted caption tone, for notes that sit outside a SettingField. */

.set-hint { font-size: var(--text-meta); line-height: 1.55; color: var(--text-tertiary); }

/* A section's own action row (e.g. the password form's submit). On a phone the
   button goes full-width at a 44px target — the same rule SettingsSaveBar and
   ScreenHeader actions follow, so no settings button is ever a thumb-miss. */

.set-actions { display: flex; gap: var(--space-2); margin-top: var(--space-4); }

@media (max-width: 768px) {
  .set-actions { width: 100%; }
  .set-actions .btn { flex: 1; min-height: 44px; }
}

/* The build id: selectable, monospace, never wrapped mid-hash. */

.set-version {
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-subtle);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--text-meta);
  letter-spacing: 0.02em;
  user-select: all;
  direction: ltr;
  unicode-bidi: isolate;
}

/* A destructive section states its nature in the heading, not in a banner. */

.set-section--danger .section-head h2 { color: var(--danger); }

.set-danger-btn { color: var(--danger); border-color: var(--danger); }

/* .set-notif-* removed 2026-07-28: a notification type is a SettingsRow now,
   the same shape every other settings list uses. What went was a bordered
   mini-card holding three stacked blocks (head, a full-width recipients
   button, a labelled mode control) nested inside the section card - three
   levels of box, and the label «وضع التنبيه» printed once per row, seven
   times down one card. */

/* ── Recipient picker (inside the shared ResponsiveModal) ─────────────────── */

.set-picker__hint {
  padding: var(--space-2-5);
  margin-bottom: var(--space-2);
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-subtle);
  font-size: var(--text-meta);
  line-height: 1.6;
  color: var(--text-tertiary);
}

.set-picker__empty { padding: var(--space-3); font-size: var(--text-meta); color: var(--text-tertiary); }

.set-picker__list { display: grid; gap: 2px; }

.set-picker__item {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  width: 100%;
  min-height: 48px;
  padding: var(--space-2) var(--space-2-5);
  border: 0;
  border-radius: var(--radius-sm, 8px);
  background: transparent;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.set-picker__item.is-picked { background: var(--accent-soft); }

.set-picker__item-body { flex: 1; min-width: 0; display: grid; }

.set-picker__item-name {
  font-size: var(--text-sm);
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.set-picker__item-kind { font-size: var(--text-xs); color: var(--text-tertiary); }

.set-picker__check {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--border);
  border-radius: 6px;
  color: #fff;
}

.set-picker__item.is-picked .set-picker__check { border-color: transparent; background: var(--accent); }

/* .set-badge removed 2026-07-28: it labelled ONE control («مخصص لكل يوم» next
   to the per-day button). The state belongs on the OBJECT, so it is the
   section's subtitle now and the badge had no second caller. */

.set-stepper { display: flex; align-items: center; gap: var(--space-3); }

.set-stepper__value {
  min-width: 32px;
  text-align: center;
  font-weight: 800;
  font-size: 20px;
  font-variant-numeric: tabular-nums;
}

/* Clinic identity block: logo + colours. */

.set-brand {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.set-brand__mark {
  width: 72px;
  height: 72px;
  border-radius: var(--radius);
  display: grid;
  place-items: center;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--bg-subtle);
  font-size: 28px;
  font-weight: 800;
  color: var(--text-tertiary);
}

.set-brand__mark img { width: 100%; height: 100%; object-fit: contain; }

.set-brand__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.set-brand__color {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  font-size: var(--text-meta);
  font-weight: 650;
  color: var(--text-secondary);
  cursor: pointer;
}

.set-brand__color input[type="color"] {
  width: 34px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  background: none;
  cursor: pointer;
}

.set-brand__warn {
  grid-column: 1 / -1;
  font-size: var(--text-meta);
  color: var(--danger);
}

@media (max-width: 600px) {
  .set-brand { grid-template-columns: minmax(0, 1fr); }
  .set-brand__mark { width: 60px; height: 60px; }
}

/* ── The save bar ──────────────────────────────────────────────────────────
   Always present, so a settings form visibly HAS a save button. It only goes
   sticky + elevated once there is something to save, so a clean form does not
   permanently spend a strip of screen. On a phone it stays in flow: the docked
   action pill (ADR-288) already carries the save within thumb reach, and two
   competing bars at the bottom of a phone is one too many. */

.set-savebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-card);
}

.set-savebar.is-dirty {
  position: sticky;
  bottom: var(--space-3);
  z-index: 5;
  border-color: var(--accent);
  box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.14));
}

.set-savebar__note { font-size: var(--text-meta); min-width: 0; }

.set-savebar__pending,
.set-savebar__clean { display: inline-flex; align-items: center; gap: var(--space-1-5); }

.set-savebar__pending { color: var(--warning); font-weight: 700; }

.set-savebar__clean { color: var(--text-tertiary); }

.set-savebar__actions { display: flex; align-items: center; gap: var(--space-2); }

@media (max-width: 768px) {
  .set-savebar { position: static; }
  .set-savebar.is-dirty { position: static; box-shadow: none; }
  .set-savebar__actions { width: 100%; }
  .set-savebar__actions .btn { flex: 1; min-height: 44px; }
}

/* ── Settings row ───────────────────────────────────────────────────────────
   A LIST row inside a section: a paired display, an uploaded track, a video
   clip, a knowledge entry. Nine of these were hand-rolled across four files in
   five spacing dialects, which is why the tips list and the audio list right
   below it did not line up. Hairline on top, not around: consecutive rows read
   as one list instead of a stack of boxes. */

.set-row {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: var(--space-2-5) 0;
  border-top: 1px solid var(--border);
  min-width: 0;
}

.set-form .set-row:first-of-type,
.set-section .set-row:first-of-type { border-top: 0; }

.set-row__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm, 8px);
  background: var(--bg-subtle);
  color: var(--text-secondary);
}

.set-row__text { flex: 1 1 auto; min-width: 0; }

.set-row__title {
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.set-row__sub {
  font-size: var(--text-meta);
  color: var(--text-tertiary);
  margin-top: 2px;
  line-height: 1.55;
  overflow-wrap: anywhere;
}

.set-row__meta { flex: 0 0 auto; }

.set-row__actions { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-2); }

@media (max-width: 600px) {
  .set-row { flex-wrap: wrap; }
  .set-row__text { flex: 1 1 100%; order: 1; }
  .set-row__icon { order: 0; }
  .set-row__meta { order: 2; }
  .set-row__actions { order: 3; margin-inline-start: auto; }
  /* 44, not 40. A row action is a real target on a phone — this is the same
     floor `.set-form .btn` sets, restated because the two-class selector here
     outranks it and a 40 would silently win. */
  .set-row__actions .btn { min-height: 44px; }
}

/* ── Settings note ──────────────────────────────────────────────────────────
   The explanatory block: "still being provisioned", "do not mirror the
   reception screen", "your plan does not include this". A tinted STRIP with an
   inline-start accent — never a full-bleed coloured banner card (owner rule:
   الحالة على العنصر, not a poster above it). */

.set-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2-5);
  padding: var(--space-3);
  margin-top: var(--space-3);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--text-tertiary);
  border-radius: var(--radius);
  background: var(--bg-subtle);
}

.set-note__icon { flex: 0 0 auto; display: inline-flex; margin-top: 1px; color: var(--text-tertiary); }

.set-note__text { min-width: 0; }

.set-note__title { font-weight: 700; font-size: var(--text-sm); color: var(--text-primary); }

.set-note__body {
  font-size: var(--text-meta);
  color: var(--text-secondary);
  line-height: 1.65;
  margin-top: 3px;
  overflow-wrap: anywhere;
}

.set-note--info    { border-inline-start-color: var(--accent); }

.set-note--info .set-note__icon { color: var(--accent); }

.set-note--warn    { border-inline-start-color: var(--warning); }

.set-note--warn .set-note__icon { color: var(--warning); }

.set-note--danger  { border-inline-start-color: var(--danger); }

.set-note--danger .set-note__icon { color: var(--danger); }

.set-note--success { border-inline-start-color: var(--success); }

.set-note--success .set-note__icon { color: var(--success); }

/* ── The ONE modal footer ───────────────────────────────────────────────────
   Five hand-written flex rows disagreed about order, justification and whether
   cancel was `outline` or `ghost`; two of them pushed the save button off a
   phone sheet. `extra` (a revoke/delete action) sits at the inline-START, the
   commit pair at the inline-END, and on a phone they stack full-width. */

.set-modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  flex-wrap: wrap;
}

.set-modal-foot__extra { flex: 0 1 auto; min-width: 0; }

.set-modal-foot__main {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-start: auto;
}

@media (max-width: 600px) {
  .set-modal-foot__extra,
  .set-modal-foot__main { flex: 1 1 100%; margin-inline-start: 0; }
  .set-modal-foot__extra .btn { width: 100%; }
  .set-modal-foot__main .btn { flex: 1; min-height: 44px; }
}

/* ── Pieces used by ONE section each, kept here so the settings vocabulary
   lives in one place instead of five inline style objects. ───────────────── */

/* Search that heads a settings list (watchers, per-service rates). */

.set-search { margin-bottom: var(--space-3); }

/* An inline "type + add" pair (a tip, a tag). */

.set-form .set-inline .input { flex: 1 1 200px; min-width: 0; }

/* A right-to-left-safe monospace path/URL echo under an action. */

.set-hint--ltr { direction: ltr; unicode-bidi: isolate; align-self: center; }

/* .set-theme-dot removed 2026-07-28: the colour moved INSIDE the select's
   options. The swatch beside the box needed a flex wrapper, which made that
   field taller than its neighbour on the same grid row — an alignment cost for
   decoration the theme names already carry. */

/* Volume-style range row. */

.set-range { display: flex; align-items: center; gap: var(--space-3); }

.set-range input[type="range"] { flex: 1 1 120px; min-width: 0; }

.set-range__value { font-variant-numeric: tabular-nums; min-width: 34px; text-align: center; }

/* Permission checkboxes (staff + doctor login modals). */

.set-perm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-2);
}

.set-perm {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2-5) var(--space-3);
  min-height: 44px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: pointer;
}

.set-perm.is-on { border-color: var(--accent); background: var(--accent-soft); }

.set-perm.is-disabled { cursor: not-allowed; opacity: 0.6; }

.set-perm input[type="checkbox"] { accent-color: var(--accent); flex: 0 0 auto; width: 18px; height: 18px; }

/* ── The person-modal body ──────────────────────────────────────────────────
   Cards inside the sheet (owner 2026-07-28): each group of a person's data is
   a SectionCard, and the door it opens — «تخصيص» for the hours, the per-service
   rates, «فتح» for the public profile — is that card's OWN head CTA, in the
   same slot and the same `btn ghost sm` skin every card on every screen uses.
   Before this the groups were bare legends and the doors were `btn outline sm`
   buttons floating at the far edge of a row, disconnected from the thing they
   opened. The four person modals were flat runs of 4-6 unrelated fields (a name
   beside a password beside a commission rate) with nothing saying which
   belonged together, which is exactly why a doctor's identity had drifted
   across three separate modals. */

.set-modal-form { display: grid; gap: var(--space-3); }

/* The sheet already supplies the outer padding, so a card in here sits flush
   and never adds a second margin under itself. */

.set-modal-form > .set-section { margin-bottom: 0; }

/* A card with only a head (hours / public profile) must not leave an empty
   body gap under its title. */

.set-modal-form > .set-section > .section-head:last-child { margin-bottom: 0; }

/* A form nested inside a section (add a knowledge entry). */

.set-subform {
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-subtle);
}

.set-subform .set-actions { margin-top: var(--space-3); }

/* A watcher row plus its numbers drawer. */

.set-watcher + .set-watcher { border-top: 0; }

.set-watcher__numbers {
  padding: var(--space-3);
  margin-bottom: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-subtle);
}

.set-watcher__numbers .set-actions { margin-top: var(--space-3); }

/* Install-guide steps (waiting-room display). */

.set-guide { display: flex; flex-wrap: wrap; gap: var(--space-5); }

.set-guide-step { flex: 1 1 220px; min-width: 0; }

.set-guide-step__head { display: flex; gap: var(--space-2); align-items: baseline; margin-top: var(--space-2); }

.set-guide-step__n {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-weight: 900;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.set-guide-step__text { min-width: 0; }

.set-guide-step__title { font-weight: 700; font-size: var(--text-sm); }

.set-guide-step__body { font-size: var(--text-meta); line-height: 1.7; color: var(--text-secondary); }

/* Person tiles (الفريق والأطباء) — ONE card for a doctor AND for a staff
   member. A portrait tile, not a table row: it carries a colour, an avatar and
   a live status. Its actions live in the app-wide ⋮ menu pinned to the corner,
   so a nine-doctor clinic paints nine kebabs instead of twenty-seven buttons. */

.set-person-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
  gap: var(--space-3);
}

.set-person {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-1);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-card);
  min-width: 0;
}

/* The ⋮ sits in the card's inline-END top corner in both directions (owner
   2026-07-28) — the far corner from where the eye starts reading, which is
   where every other overflow menu in this app lives. It is an affordance, not
   content, so it must never push the name off centre. */

.set-person__menu { position: absolute; inset-block-start: var(--space-2); inset-inline-end: var(--space-2); }

.set-person__avatar { margin-bottom: var(--space-2); }

.set-person__name { font-weight: 800; font-size: var(--text-md); overflow-wrap: anywhere; }

.set-person__sub {
  font-size: var(--text-meta);
  color: var(--text-tertiary);
  overflow-wrap: anywhere;
  direction: ltr;
  unicode-bidi: plaintext;
}

.set-person__meta { font-size: var(--text-meta); color: var(--success); font-weight: 700; direction: ltr; unicode-bidi: plaintext; }

.set-person__foot {
  font-size: var(--text-meta);
  color: var(--text-tertiary);
  line-height: 1.7;
  margin-top: var(--space-1);
  min-height: 34px;
}

.set-person .chip { margin-top: var(--space-2); }

/* The way back out of a section. Sits under the header, reads as a return
   path, and is phone-only because the desktop rail never leaves the screen. */

.settings-back {
  display: none;
}

@media (max-width: 768px) {
  .settings-back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1-5);
    margin-bottom: var(--space-3);
    padding: var(--space-2) var(--space-3);
    min-height: 44px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--bg-card);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 650;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .settings-back:active { transform: scale(0.97); }
  /* RTL: the "back" glyph must point the way the reader came FROM. */
  [dir="ltr"] .settings-back svg { transform: scaleX(-1); }
}

/* ════════════════════════════════════════════════════════════════════
   V-Interaction amendment #3 (ADR-064) — backdrop-filter prefix
   survival via @supports query.

   ROOT CAUSE (confirmed via owner's Chrome DevTools snapshot on
   modirify.com: `backdrop_filter:'none', webkit_backdrop_filter:undefined`):
   Vite/esbuild's CSS minifier dedupes adjacent vendor-prefix pairs
   (treats `-webkit-backdrop-filter` and `backdrop-filter` as the same
   logical property, keeps only the last). With source order
   `backdrop-filter` first then `-webkit-` second, the bundle ends with
   ONLY `-webkit-backdrop-filter` — which modern Chrome (114+) no longer
   recognizes, so backdrop-filter computes to `none` → no blur.

   FIX: Re-declare every backdrop-filter target inside an `@supports`
   block. esbuild can't merge across @supports boundaries, so the
   unprefixed declaration survives. Modern Chrome (Chrome 76+ supports
   unprefixed) resolves via the @supports rule; iOS Safari < 18 (which
   doesn't recognize unprefixed) skips the @supports block and falls
   back to the `-webkit-backdrop-filter` declaration that survives in
   the primary rules above. Both browser tracks get blur.

   Each declaration mirrors the EFFECTIVE cascade value from the primary
   rules above (e.g. .modal-backdrop's primary rule L962 has
   blur(var(--glass-blur-sm)) but a later override at L5024 wins with
   blur(8px) saturate(140%), so this block uses the override value).
   ════════════════════════════════════════════════════════════════════ */

/* ────────────────────────────────────────────────────────────────────
   V-Interaction amendment #4 — suppress nested .fade-in animation
   when a screen wrapper is rendered INSIDE a .gm-box modal.

   Many screens (NewAppointment, NewInvoice, etc.) wrap their root in
   `<div className="fade-in">` so they get a soft entrance animation
   when used as a standalone route. The same screens are ALSO used
   as modal children (Reception's "موعد جديد" mounts NewAppointment
   inside .gm-box, etc.). In the modal case, the outer .gm-box already
   runs its own modalIn animation (translateY 18px → 0 + scale 0.97 → 1
   over 280ms). Nesting the inner .fade-in's translateY 4px → 0 (250ms)
   compounds the transforms — the child briefly sits 4px lower than its
   parent and finishes 30ms earlier, producing a visible "stretch" / "امتداد"
   glitch the owner reported.

   Fix: drop the inner animation entirely when wrapped in .gm-box. The
   outer modalIn handles entrance opacity + position. Standalone-route
   usage of the same screens is untouched.
   ──────────────────────────────────────────────────────────────────── */

.gm-box .fade-in {
  animation: none;
}

@supports (backdrop-filter: blur(0)) {
  .modal-backdrop                       { backdrop-filter: blur(8px) saturate(140%); }
  .modal-backdrop:has(.confirm-dialog)  { backdrop-filter: blur(8px) saturate(140%); }
  .modal, .modal-box                    { backdrop-filter: blur(var(--glass-blur)); }
  .accounting-modal                     { backdrop-filter: blur(var(--glass-blur)) !important; }
  .gm-bd                                { backdrop-filter: blur(var(--glass-blur-sm)) !important; }
  .gm-box                               { backdrop-filter: blur(var(--glass-blur)) !important; }
    .topbar                               { backdrop-filter: blur(14px) saturate(180%); }
}

/* ════════════════════════════════════════════════════════════════════
   V-Mobile (ADR-065) — Wave 3 third track (Phase 5 closer).

   Closes the "mobile coverage only at shell layer (U-7)" finding from
   ADR-047 by filling per-screen + primitive responsive gaps:

   /1 — Primitive metric-tile phone compression.
   /2 — Legacy rich-empty maps padding compression (≤480px) share the
        recipe: .wa-empty / .fin-empty /
        .kanban-empty-rich (.mk2-col-empty retired, R2 W3). ADR-060 §2 formalized .wa-empty as the
        de-facto shell; here we add its first cross-map phone-padding
        ruleset.
   /3 — PatientFile central medical-alert strip phone wrapping.
   /4 — WhatsApp adds phone rules below the existing 900 breakpoint
        (.wa-stats + .wa-template-editor already covered at 900):
        .wa-rule-card + .wa-status-card + .wa-schedule-bar.
   /5 — Touch targets WCAG AA (44×44) on phones (≤768px). The existing
        .btn min-height: 40px at L2036 was 4px short; .icon-btn + .tab
        had no explicit phone height. Lifts all interactive primitives
        to WCAG minimum on phones; desktop unchanged.

   Phone-breakpoint convention is 480px (phone) + 768px (compact /
   tablet portrait) per ADR-024 Decision F. NO new breakpoints. Each
   sub-block stacks AFTER existing rules so the cascade resolves on
   our values for matching media.

   Why a single appended block (instead of editing in place near each
   primitive): mirrors V-Interaction's appended @supports block —
   keeps the V-X provenance auditable in one place + avoids cascade
   ordering surprises if a future audit reverts a sub-section.
   ════════════════════════════════════════════════════════════════════ */

/* V-Mobile/1 — Primitive phone polish (≤480px) */

@media (max-width: 480px) {
  .metric-tile { padding: 14px 14px; gap: var(--space-2); }
  .metric-tile-value { font-size: 24px; }
  .metric-tile-icon { width: 32px; height: 32px; font-size: 16px; }
  .metric-tile-label { font-size: var(--text-xs); }
}

/* V-Mobile/2 — Rich-empty maps phone padding (≤480px) */

@media (max-width: 480px) {
  .wa-empty,
  .fin-empty,
  .kanban-empty-rich {
    padding: 22px 14px;
    gap: var(--space-2-5);
  }
  .wa-empty-icon,
  .fin-empty-icon,
  .kanban-empty-rich .kanban-empty-icon {
    width: 44px;
    height: 44px;
    font-size: 20px;
  }
  .wa-empty-title,
  .fin-empty-title,
  .kanban-empty-rich .kanban-empty-title {
    font-size: var(--text-base);
  }
  .wa-empty-hint,
  .fin-empty-hint,
  .kanban-empty-rich .kanban-empty-hint {
    font-size: var(--text-meta);
    max-width: none;
  }
}

/* V-Mobile/3 — PatientFile fills the 980→640 gap at 768 + adds phone refinements */

@media (max-width: 768px) {
  .pf-hero-alerts {
    gap: var(--space-1-5);
    padding-top: 10px;
    margin-top: 12px;
  }
  .pf-hero-alerts__label {
    width: 100%;
    font-size: var(--text-xs);
    margin-bottom: 4px;
  }
}

/* V-Mobile/5 — Touch targets WCAG AA (44×44) on phones */

@media (max-width: 768px) {
  .btn {
    min-height: 44px;
    padding: 10px 14px;
  }
  .btn.icon,
  .icon-btn {
    min-width: 44px;
    min-height: 44px;
  }
  .tabs .tab {
    min-height: 44px;
    padding: 12px 14px;
  }
}

/* ============================================================
   V-Mobile/6 (Phase 1.5 — 2026-05-26) — Comprehensive mobile coverage
   ============================================================
   STATUS (Wave 7 closer 2026-05-27): Phase 1.5 fully shipped. Most
   of the rules below are now SAFETY NETS for legacy code paths that
   haven't been migrated to the V-Responsive primitives yet (or that
   intentionally keep the legacy CSS classes like .data-table). The
   Wave 5/5b/5c/5d/5e/5f/5g/5h/6 runs added explicit class-based
   responsive rules elsewhere in this file that supersede most of the
   broad selectors here. Per-rule supersession notes inline below.

   Original audit found that V-Mobile/1-5 covered the shell +
   primitives but missed screen-level patterns. This block fills the
   gaps:
   /1 — Tables on phones: parent gets horizontal scroll via :has(),
        table itself stays readable with reduced padding/font.
        SUPERSEDED for Wave 2 list screens (they use DataTable +
        mobileCard now). Still active as a safety net for any
        remaining raw .data-table consumers.
   /2 — Grid KPI tiles (.grid.cols-N) collapse one step on tablets
        and fully on phones.
        SUPERSEDED for Wave 4a/4c grids that migrated to
        .l-grid--fixed-N (which has its own breakpoint behavior).
        Still active for any remaining .grid.cols-N consumers.
   /3 — REMOVED in 2026-05-26 (see /3 comment below).
   /4 — Modals/forms: full-width on phones with 0 margin so the
        usable area maximises. .modal already does 100vw-40px on
        desktop; phone shrinks the margin to 0 + corners squared.
        SUPERSEDED for Wave 3 + Wave 5d-5h modals that migrated to
        ResponsiveModal (which is bottom-sheet on phones). Still
        active for the legacy shell Modal primitive (used in 2
        places still — feature comparison + upgrade flow migrated
        in Wave 5g, but ResponsiveModal preserves the CSS class
        names for backward compat with any future raw consumer).
   /5 — Reception kanban: .kanban-full-width 5-column row becomes
        vertically scrolling on tablet (<=980px) and vertically
        stacked on phone (<=480px). EXTENDED by Wave 5b which adds
        a more direct .kanban-board stack rule + sticky col headers.
   /6 — Appointments calendar: tighter cells + smaller text.
        EXTENDED by Wave 5c (.appt-week-grid stack at <=480px) +
  .data-table {
    min-width: 560px;
    font-size: 13px;
  }
  .data-table thead th,
  .data-table tbody td {
    padding: 10px 10px;
    white-space: nowrap;
  }
}

@media (max-width: 480px) {
  .data-table {
    font-size: 12px;
    min-width: 480px;
  }
  .data-table thead th,
  .data-table tbody td {
    padding: 8px 8px;
  }
}

/* /2 — Grid KPI / card tiles collapse one step. */

@media (max-width: 768px) {
  .grid.cols-4 {
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 12px !important;
  }
}

@media (max-width: 480px) {
  .grid.cols-2,
  .grid.cols-4 {
    grid-template-columns: 1fr !important;
    gap: 10px !important;
  }
}

/* /3 — REMOVED (2026-05-26).
   Originally used [style*="grid-template-columns: ..."] !important to
   force inline grid templates to collapse on mobile. Removed because:
   (a) too broad — hit unintended elements with similar inline styles.
   (b) anti-pattern — fighting React inline styles with CSS !important
       is symptom-fix; the real fix is to refactor screens to use the
       Layout Primitives (.l-grid / .l-sidebar / .l-switcher — see
       V-Responsive section below). Migration plan in MOBILE_ARCHITECTURE.md.
   Screens that depended on /3 will be migrated one-by-one. Until a
   given screen is migrated, mobile users see the desktop inline grid
   (which IS suboptimal but not broken — the grid wraps via flex-wrap
   or scrolls within its container in most cases). */

/* /4 — Modals: maximise usable area on phones. */

@media (max-width: 480px) {
  .modal,
  .modal-box,
  .modal-lg,
  .modal-xl {
    width: 100vw !important;
    max-width: 100vw !important;
    max-height: 100dvh !important;
    height: 100dvh;
    border-radius: 0 !important;
    margin: 0 !important;
  }
  .modal-header,
  .modal-footer { padding: 14px 16px; }
  .modal-body { padding: var(--space-4); }
  .modal-footer .btn { flex: 1 1 auto; }
}

/* /5 — Reception kanban responsive.
   RETIRED 2026-07-05 (Nav-Kanban consolidation). The ≤980px
   horizontal-scroll (280px columns) + ≤480px flex-column rules were
   overridden by the adaptive `.kanban-board.kanban-full-width` tiers
   and only added cascade fragility. Live model: the "V-Phase2 Wave 2
   — Adaptive kanban" block (chips ≤600 / 2-col 601-768 / 3-col
   769-1023 / 5-col ≥1024). */

/* /6 — Appointments calendar: tighter cells + smaller text.
   Day/week/month views all need to shrink. */

@media (max-width: 480px) {
  /* Day view: rooms grid scrolls horizontally instead of squishing */
  [style*="80px repeat("] {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* /8 — Marketing screen + Settings tabs.
   Wave 7 cleanup: .settings-tabs selector removed because Wave 6
   shipped a different pattern (.settings-tabs-mobile, horizontal
   Tabs strip via the Tabs primitive). No DOM element carries the
   .settings-tabs class anymore. The .sub-tabs scroll rule stays
   because some sub-navigation surfaces still use it. */

@media (max-width: 768px) {
  .sub-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
    scrollbar-width: thin;
  }
  .sub-tabs > * { flex-shrink: 0; }

}

/* /9 — (RETIRED R3 Phase 3, 2026-07-11) the hero action-row phone rules lived on
   .dash-hero-actions; that shell is gone. The clinical patient header's icon-only
   .pf-hero-actions wrap on their own (fixed-size icons, flex-wrap). */

/* /10 — Inputs and selects WCAG-AA height on phones. 16px font
   size to prevent iOS auto-zoom on focus. */

@media (max-width: 768px) {
  .input,
  .textarea,
  .select,
  input[type="text"],
  input[type="number"],
  input[type="tel"],
  input[type="email"],
  input[type="date"],
  input[type="time"],
  textarea,
  select {
    min-height: 44px;
    font-size: 16px;
  }
}

/* ============================================================
   V-Responsive (Phase 1.5 architectural — 2026-05-26)
   ============================================================
   Layout Primitives + Container Queries + Mobile-First Components.
   Inspired by Heydon Pickering's "Every Layout" — algebraic CSS
   patterns that collapse naturally based on container space, NOT
   viewport breakpoints. Screens compose these primitives instead
   of writing per-screen @media rules.

   See MOBILE_ARCHITECTURE.md for the full guide + migration plan.

   §1  Layout Primitives — composable boxes:
        .l-stack    — vertical rhythm with consistent gap
        .l-cluster  — horizontal wrap with consistent gap
        .l-switcher — horizontal → vertical at a threshold
        .l-grid     — auto-fit grid with sensible minmax
   (.l-sidebar / .l-center / .l-cover were part of the original set but
    no screen ever adopted them; their rules were removed 2026-07-19.)
   §2  Container Queries — cards adapt to their OWN width
   §3  Responsive Table card view — see also data-table.jsx
   §4  Responsive Modal — see also responsive-modal.jsx
   ============================================================ */

/* §1.1 Stack — vertical flex with token-scaled gap.
   Use for: any vertical list of children (form fields, card
   contents, page sections). Replaces ad-hoc <div style={{
   display: 'flex', flexDirection: 'column', gap: 12 }}>. */

.l-stack {
  display: flex;
  flex-direction: column;
  gap: var(--l-stack-gap, var(--space-fluid-md));
}

.l-stack--xs { --l-stack-gap: var(--space-fluid-xs); }

.l-stack--sm { --l-stack-gap: var(--space-fluid-sm); }

.l-stack--lg { --l-stack-gap: var(--space-fluid-lg); }

.l-stack--xl { --l-stack-gap: var(--space-fluid-xl); }

/* §1.2 Cluster — horizontal flex with wrap. The defining feature:
   every child is on a separate text-line if it can't fit; never
   horizontal scroll. Use for: button rows, chip groups, breadcrumbs,
   filter pills, hero action buttons. */

.l-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--l-cluster-gap, var(--space-fluid-sm));
  align-items: center;
}

.l-cluster--center  { justify-content: center; }

.l-cluster--end     { justify-content: flex-end; }

.l-cluster--between { justify-content: space-between; }

.l-cluster--top     { align-items: flex-start; }

/* §1.3 Switcher — flex row that becomes flex column when its
   container is narrower than a threshold. The threshold is set
   via --l-switcher-threshold (default 30rem = 480px). No @media
   query needed — pure CSS algebra via flex-basis calc().
   How it works: when (threshold - 100%) is positive, flex-basis
   is huge → items wrap to single column. When negative, basis is
   0 → items align horizontally and grow to fill.
   Use for: 2-column layouts that should stack on narrow widths. */

.l-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: var(--l-switcher-gap, var(--space-fluid-md));
}

.l-switcher > * {
  flex-grow: 1;
  flex-basis: calc((var(--l-switcher-threshold, 30rem) - 100%) * 999);
}

/* §1.4 Sidebar — main content + sidebar where the sidebar drops
   below the main on narrow containers. Sidebar width = --l-sidebar-width
   (default 20rem = 320px). First child = main content, second =
   sidebar. Add .l-sidebar--reverse to swap. */

/* §1.5 Grid — auto-fit responsive grid. Each cell has a minimum
   width set via --l-grid-min (default 14rem). The grid figures out
   how many columns fit and wraps the rest. The min(100%, ...)
   ensures the grid never overflows its container even when the
   minmax-min is larger than the container itself (key gotcha). */

.l-grid {
  display: grid;
  gap: var(--l-grid-gap, var(--space-fluid-md));
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, var(--l-grid-min, 14rem)), 1fr)
  );
}

.l-grid--sm   { --l-grid-min: 10rem; }

.l-grid--md   { --l-grid-min: 14rem; }

.l-grid--lg   { --l-grid-min: 18rem; }

.l-grid--card { --l-grid-min: 16rem; }

/* §1.5b Fixed-count grids for HERO KPI rows. When you have exactly
   N tiles and want them to all fit in a single row at tablet+ widths
   (regardless of available space), use these instead of auto-fit.
   Pattern:
     - phone (< 30em / 480px): 1 column (stacked)
     - phone-landscape + tablet+ (>= 30em): N equal columns
   Solves the iPad-portrait 2+1 problem with auto-fit when the
   sidebar squeezes the main area below 3 * --l-grid-min. */

.l-grid--fixed-2,
.l-grid--fixed-3,
.l-grid--fixed-4 {
  display: grid;
  gap: var(--l-grid-gap, var(--space-fluid-md));
  grid-template-columns: 1fr;
}

@media (min-width: 30em) {
  .l-grid--fixed-2 { grid-template-columns: repeat(2, 1fr); }
  .l-grid--fixed-3 { grid-template-columns: repeat(3, 1fr); }
  .l-grid--fixed-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 48em) {
  .l-grid--fixed-4 { grid-template-columns: repeat(4, 1fr); }
}

/* §1.8 Tabs — the .l-tabs strip was removed 2026-07-19: ScreenTabBar
   (app/src/ui/screen-tab-bar.jsx -> .screen-tabbar) is the tab strip the
   app actually renders, and two rival implementations is the duplication
   this cleanup exists to remove. */

/* §2 Container Queries — cards adapt to their OWN width.
   Applied today by .appt-agenda-card (the .l-card-container wrapper was
   never adopted and was removed 2026-07-19). When a wrapper declares
   container-type: inline-size, children using
   @container queries respond to that wrapper's width, NOT the
   viewport. Lets a card render dense when in a sidebar (narrow)
   but spacious when in the main canvas (wide).
   Browser support: Chrome 105+, Safari 16+, Firefox 110+ — all
   covered by the chrome87 build target with chrome 109+ devices. */

.l-card-grid-2 {
  display: grid;
  grid-template-columns: 1fr;  /* default: 1 column on narrow */
  gap: var(--space-fluid-sm);
}

/* §3 Responsive Table card view — for DataTable's mobile mode.
   The DataTable primitive (app/src/ui/data-table.jsx) renders a
   normal <table> on tablet+ and a stack of cards on phone. The
   <table> gets .data-table-desktop (visible ≥ 768px), the card
   list gets .data-table-cards (visible < 768px). Default: cards
   hidden, table visible. */

.data-table-cards {
  display: none;
}

@media (max-width: 47.99em) {  /* < 768px */
  .data-table-desktop {
    display: none;
  }
  .data-table-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-fluid-sm);
  }
}

.dt-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-fluid-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-fluid-xs);
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}

.dt-card--clickable {
  cursor: pointer;
}

.dt-card--clickable:hover,
.dt-card--clickable:focus-visible {
  transform: translateY(var(--lift-1));
  box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.18);
  border-color: var(--accent);
  outline: none;
}

.dt-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-fluid-sm);
}

.dt-card-title {
  font-size: var(--text-md);
  font-weight: 800;
  color: var(--text-primary);
  line-height: var(--leading-snug);
}

.dt-card-subtitle {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  line-height: var(--leading-snug);
}

.dt-card-aside {
  flex-shrink: 0;
}

.dt-card-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-fluid-3xs) var(--space-fluid-sm);
  margin: 0;
  padding-top: var(--space-fluid-2xs);
  border-top: 1px dashed var(--border);
  font-size: var(--text-sm);
}

.dt-card-meta dt {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-weight: 700;
  align-self: center;
}

.dt-card-meta dd {
  margin: 0;
  font-weight: 600;
  color: var(--text-primary);
  align-self: center;
}

/* Wave 2a (V-Patients/4): touch-friendly action footer at the bottom
   of mobile cards. Renders as a row of full-width buttons separated
   by a dashed border. */

.dt-card-footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-fluid-xs);
  padding-top: var(--space-fluid-xs);
  border-top: 1px dashed var(--border);
  margin-top: var(--space-fluid-2xs);
}

.dt-card-footer > * {
  flex: 1 1 auto;
  min-height: 40px;  /* WCAG-friendly tap target */
}

/* Expandable rows (DataTable `renderDetail`, 2026-08-06). A row that
   summarises its own lines opens them in place: the drawer is a full-width
   cell under the row, inset so the nested table reads as CONTENTS of the row
   above rather than as a sibling of it. */

.dt-detail-row > td {
  border-top: 0;
  box-shadow: inset 0 1px 0 var(--border);
}

.dt-detail-row .data-table-wrapper,
.dt-detail-row table {
  margin: 0;
}

/* The nested table's own chrome is redundant inside a drawer that already
   has a background and a border - two nested frames read as a mistake. */

.dt-detail-row table {
  background: transparent;
  box-shadow: none;
  border: 0;
}

.dt-detail-row td,
.dt-detail-row th {
  font-size: 12px;
}

/* Phone: the drawer lives inside the card instead of under a row. */

.dt-card-detail {
  padding-top: var(--space-fluid-xs);
  margin-top: var(--space-fluid-2xs);
  border-top: 1px dashed var(--border);
}

/* §4 Responsive Modal — bottom-sheet on phone, centered on tablet+.
   Used by app/src/ui/responsive-modal.jsx. See that file for the JSX. */

.rm-backdrop {
  position: fixed;
  inset: 0;
  /* ADR-139 Wave 1 (2026-05-28): scrim deepened to .55 + blur lifted to
     12px so the modal floats over a softer, more cinematic backdrop —
     matches the Ctrl+K command palette feel. */
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: var(--z-modal-backdrop);
  padding: 0;
  animation: rm-backdrop-in 220ms ease-out;
}

.rm-backdrop.is-exiting {
  animation: rm-backdrop-out 180ms ease-out forwards;
}

@keyframes rm-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes rm-backdrop-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* ADR-139 Wave 0 (2026-05-28): premium shell is now the default.
   What used to be opt-in `.rm-card--premium` is now baked into the
   base `.rm-card` so every modal automatically reads as "Ctrl+K-style"
   premium: deeper float shadow, larger desktop radius, deeper body
   padding, soft accent input glow, primary CTA hover lift.
   The `.rm-card--premium` modifier class is kept as a no-op alias
   for back-compat with any consumer that still passes it.
   ADR-139 Wave 11 (2026-05-28): card now uses the glass surface
   tokens — same recipe as the Ctrl+K command palette. The
   semi-transparent bg + heavy backdrop-blur turns the modal into a
   true floating glass panel over the dimmed scrim. Subtle border
   highlight mimics the cmdk panel. Text contrast stays AAA because
   the glass tokens are tuned at ~88-92% opacity. */

.rm-card {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  width: 100%;
  max-height: 95dvh;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--glass-shadow);
  /* ADR-139 Wave 8 (2026-05-28): position context for the drag handle
     so it can sit on top of the card without taking flex slot space. */
  position: relative;
  /* ADR-139 Wave 1 (2026-05-28): entrance curve aligned with the
     Ctrl+K command palette — cubic-bezier(0.34, 1.32, 0.64, 1) is the
     gentle-overshoot spring that makes the modal land softly into
     place. Same curve on mobile bottom-sheet + desktop scale. */
  animation: rm-card-in-mobile var(--modal-in) var(--modal-in-ease) var(--modal-lead) backwards;
}

.rm-backdrop.is-exiting .rm-card {
  animation: rm-card-out-mobile 180ms ease-in forwards;
}

@keyframes rm-card-in-mobile {
  from { transform: translateY(40px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

@keyframes rm-card-out-mobile {
  from { transform: translateY(0); opacity: 1; }
  to   { transform: translateY(28px); opacity: 0; }
}

@media (min-width: 48em) {  /* 768px+ */
  .rm-backdrop {
    align-items: center;
    padding: var(--space-fluid-lg);
  }
  .rm-card {
    width: min(100%, var(--rm-max-width, 600px));
    max-height: calc(100dvh - 4rem);
    border-radius: var(--radius-modal-premium);
    /* ADR-139 Wave 1: same overshoot curve as the mobile sheet for a
       continuous feel across breakpoints; tightened from 280ms to
       240ms so desktop pops faster (less wait on hover-to-click). */
    animation: rm-card-in-desktop var(--modal-in) var(--modal-in-ease) var(--modal-lead) backwards;
  }
  .rm-backdrop.is-exiting .rm-card {
    animation: rm-card-out-desktop 160ms ease-in forwards;
  }
  /* W-DETAIL (owner 2026-07-17): "لما بيتفتح بحس انه بيرفرش وبيفتح مودالين ورا
     بعض". THE geometry half of that bug. A detail modal opens before its log has
     loaded, so it mounts SHORT (a 3-row skeleton), and ~250ms later the real
     rows arrive and it grows. The backdrop centers (above), so growing by Δ
     moves the card Δ/2 UP and Δ/2 DOWN — it visibly re-lands, and it does so
     while the 240ms overshoot spring is still settling. Two landings, one open:
     that is the second modal the owner sees.
     Fix: a detail modal has a STABLE FRAME. A definite height makes the card's
     size independent of what the fetch returns; `.rm-body` (flex:1, min-height:0,
     overflow-y:auto) then scrolls inside it. The card lands exactly once, at a
     size it keeps. Desktop-only because centering is what doubles the movement —
     the phone sheet is bottom-anchored and its own rule stands.

     The NUMBER is a separate question from the technique: what stops the shudder
     is that the height is DEFINITE, not that it is small. At 78vh a record left
     a fifth of the screen empty while its own panes scrolled, so the frame grew
     to 86vh (owner 2026-07-19). Still definite, still one landing. */
  .rm-card.detail-modal { height: min(86vh, 720px); }
  /* W-DETAIL v2 (owner 2026-07-17): "المودال بيتمدد لما الارقام تظهر". The card
     HEIGHT is already fixed (above), yet the two-pane body still RE-LAID-OUT when
     the async log went from a 3-row skeleton to N real rows: l-grid--fixed-2
     stretches both cells to the tallest, so a long payments/sessions log stretched
     the receipt pane beside it and the whole content grew ~250ms after open -
     exactly the "expands when the numbers appear" the owner sees, worst in finance
     (longest logs). The entrance animation was NEVER the cause (owner ruled it out
     live). Root fix: the two panes FILL the fixed card height and scroll
     INDEPENDENTLY, so async rows scroll inside their own pane instead of resizing
     the grid. The modal opens at final geometry and never reflows. CSS-only; drives
     all four detail modals via the shared class, no (locked) JSX touched. */
  .rm-card.detail-modal .rm-body { overflow: hidden; }
  .rm-card.detail-modal .l-grid--fixed-2 {
    height: 100%;
    min-height: 0;
    align-items: stretch;
  }
  .rm-card.detail-modal .l-grid--fixed-2 > * {
    min-height: 0;
    max-height: 100%;
    overflow-y: auto;
    /* W-DETAIL v3 (owner 2026-07-17): "لما سجل الدفعات يحمل الكروت بتاخد حركة
       شمال". ROOT CAUSE, proven live: the pane's vertical scrollbar TOGGLES when
       the async log grows the content past the pane height (skeleton -> N rows),
       and each appear/disappear reflows the content by the scrollbar width (~10px)
       - the horizontal "jump" the owner sees, present since the modals were first
       written. Reserve the gutter ALWAYS so the scrollbar can come and go inside
       its own reserved lane and the content never shifts. */
    scrollbar-gutter: stable;
  }
  @keyframes rm-card-in-desktop {
    from { transform: scale(0.94); opacity: 0; }
    to   { transform: scale(1); opacity: 1; }
  }
  @keyframes rm-card-out-desktop {
    from { transform: scale(1); opacity: 1; }
    to   { transform: scale(0.97); opacity: 0; }
  }
}

/* ── DetailModal (W-DETAIL central component, 2026-07-17) ─────────────────────
   The ONE two-pane "record" modal (window.DetailModal). Every class below is a
   1:1 lift of the inline styles the invoice/payable/item/service detail modals
   each hand-typed, so the visual is identical while the markup collapses to the
   shared primitive. */

/* Entrance: FADE ONLY - no transform at any width - so a large record card can
   never read as "expanding" or shuddering on open (the owner's "بيتمدد لما
   الارقام تظهر"). Specificity (0,2,0) beats the base .rm-card slide AND the
   desktop rule, so it wins everywhere; the exit slide (0,3,0) is untouched. */

/* ADR-290 EXCEPTION - deliberately NOT on --modal-in, do not "unify" it.
   The detail modal mounts SHORT (skeleton) and grows ~250ms later when its log
   lands, so any TRANSFORM entrance replays against a changing height and reads
   as the double-open shudder the owner reported in W-DETAIL. A plain opacity
   fade is what fixed it. Give it more time here and the growth happens INSIDE
   the entrance, which is exactly the bug. */

.rm-card.detail-modal { animation: rm-card-fade 150ms ease-out both; }

@keyframes rm-card-fade { from { opacity: 0; } to { opacity: 1; } }

/* Two panes (LEFT receipt / RIGHT story). Layout hooks; the fixed-frame
   independent-scroll containment lives in the @media block above. */

/* ── PRE-PREMIUM language (owner 2026-07-19) ───────────────────────────────
   The premium pass (7c6dfcf5 half the modal, c4b72425 all of it) is REVERTED
   here to the plain record language `85f5b892` shipped: flat panes with a
   single seam between them, a plain section heading, undecorated fact rows,
   and one quiet card surface. Every gradient, accent hairline, inset seam
   shadow, hover lift, and uppercase eyebrow is gone.

   What is NOT reverted, and why:
   1. The ARCHITECTURE stays. `c4b72425` did two things — it made the modal
      premium AND it deleted the hand-rolled internals from five modals onto
      this one vocabulary. Only the first is a look. So `.detail-card*`,
      `.detail-section-title`, `.detail-log-title.has-action` and the central
      EmptyState tuning all survive — restyled into the plain idiom, so the
      call sites keep working and nothing goes back to being hand-typed.
   2. The RTL fixes stay. `padding-inline-end` / `inset-inline-end` on the
      timeline are logical properties replacing physical ones that reserved
      the rail's lane on the wrong side in Arabic. That was a bug, not a
      decoration.
   3. The stable-frame + independent-scroll + fade-entrance rules stay: they
      predate the premium pass (they ARE `85f5b892`) and they are what stops
      the modal shuddering on open. */

.detail-receipt {
  border-inline-end: 1px solid var(--border);
  padding: var(--space-5);
  background: var(--bg-subtle);
}

/* The record pane holds every logged section, so its rhythm decides how much of
   a record you can read without scrolling. 16px separates sections clearly at
   this type size; the 20px it started at bought nothing but a scrollbar. */

.detail-story {
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.detail-receipt-head {
  text-align: center;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--border);
}

/* A section head that follows fact rows (the patient's «السجل الطبي» under the
   identity block) needs air above it — structure, not ornament. */

.detail-receipt-row + .detail-receipt-head { margin-top: 16px; }

.detail-receipt-title { font-weight: 800; font-size: var(--text-md); }

.detail-receipt-sub { font-size: var(--text-meta); color: var(--text-secondary); margin-top: 2px; }

.detail-receipt-meta {
  font-size: var(--text-meta);
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 14px;
}

.detail-receipt-row { display: flex; justify-content: space-between; font-size: var(--text-meta); gap: var(--space-2); }

.detail-receipt-label { color: var(--text-tertiary); }

/* The pair's value sits at the inline-END. Pre-premium this fell out of the
   row's space-between alone; it is declared now because the card vocabulary
   below shares the pane, and a value must never inherit a card's start-align. */

.detail-receipt-value { text-align: end; min-width: 0; }

.detail-receipt-value.strong { font-weight: 700; }

.detail-receipt-items {
  border-top: 1px dashed var(--border);
  padding-top: 12px;
  margin-bottom: 12px;
}

.detail-receipt-total-wrap { border-top: 1px dashed var(--border); padding-top: 10px; }

.detail-receipt-total {
  display: flex;
  justify-content: space-between;
  font-size: 16px;
  font-weight: 800;
  margin-top: 4px;
}

.detail-receipt-total-value { color: var(--accent); }

/* Money summary progress bar (RIGHT pane, under the triad). */

.detail-progress { height: 8px; border-radius: var(--radius-pill); background: var(--border); overflow: hidden; }

.detail-progress-fill { height: 100%; border-radius: var(--radius-pill); transition: width .3s; }

.detail-progress-pct { font-size: var(--text-xs); color: var(--text-tertiary); text-align: end; margin-top: 4px; }

@media (prefers-reduced-motion: reduce) {
  .detail-progress-fill { transition: none; }
}

/* Timeline log (RIGHT pane, bottom). */

.detail-log { flex: 1; }

/* SECTIONS MUST READ AS SECTIONS (owner-caught 2026-07-19: «مش بيبانوا في وضع
   إن مفيش حالات»). When every log in a pane is empty, three dashed boxes with
   three headings run together as one grey field and the reader cannot tell
   where one subject ends. The receipt pane already owns the app's separator —
   a dashed rule (`.detail-receipt-head`, `.detail-receipt-items`) — so the
   story pane borrows the SAME one rather than inventing a divider. Applied
   between siblings only, so a single-log modal gains nothing.

   🔴 NO `margin-top` HERE. `.detail-story` is a flex COLUMN with its own `gap`,
   which ALREADY separates siblings; adding a margin stacks on top of it, so the
   first cut spent gap + margin + padding = 56px between every pair and a
   four-section record could not fit the pane (owner-caught 2026-07-19: «صغر
   المسافات بحيث كله يبان أول ما يفتح»). The gap owns the distance; this rule
   owns only the rule and the air it needs. */

.detail-log + .detail-log {
  padding-top: 12px;
  border-top: 1px dashed var(--border);
}

/* A section's action is the one thing you can DO with it, so it reads as a
   control: a bordered, tinted target instead of a bare glyph the eye skips.
   Scoped to the section header, so row kebabs inside tables are untouched. */

.detail-log-title.has-action .icon-btn {
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent);
  width: 34px;
  height: 34px;
}

.detail-log-title.has-action .icon-btn:hover {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}

/* THE section label of a detail modal — one class, every pane, every modal.
   `.detail-log-title` is its alias so existing logs keep working. */

.detail-section-title,
.detail-log-title { font-weight: 700; font-size: var(--text-sm); margin-bottom: 10px; }

/* With an action node the title becomes a section-header row: label at the
   inline-start, the jump-off pinned to the inline-END. `width:100%` is
   load-bearing — `.detail-log` sits in the flex COLUMN `.detail-story`, which
   would otherwise shrink-wrap this row and glue the pair together. */

.detail-log-title.has-action {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.detail-log-title.has-action > span:first-child { min-width: 0; }

.detail-log-title.has-action .btn.ghost.sm {
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 700;
}

/* An empty log renders through the app's CENTRAL EmptyState primitive, so it
   arrives with the icon chip + title anatomy every other empty surface uses.
   This class only sizes it for the narrow pane and restores the plain dashed
   well the pre-premium `.detail-log-empty` was. */

/* An empty log is a quiet dashed line, not a decorated panel. The premium pass
   routed it through the central EmptyState so it arrived with an icon chip —
   and an icon on EVERY empty section of EVERY detail modal reads as decoration
   competing with the record (owner-reverted 2026-07-19). */

/* An empty section is the LEAST important thing in a record — it says only that
   nothing is there — so it must not cost as much height as a section that has
   content. 12px keeps the well readable without letting three empties push the
   real sections off the pane. */

.detail-log-empty {
  text-align: center;
  padding: 8px 0;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  border: 2px dashed var(--border);
  border-radius: 10px;
}

/* The rail + dots need a lane, and it must be a LOGICAL one: `padding-right`
   is physical, so in RTL it reserved the lane on the wrong side of the cards
   while the rail itself drew on the other (owner-caught 2026-07-19). */

.detail-timeline { position: relative; padding-inline-end: 20px; }

.detail-timeline-line {
  position: absolute;
  inset-inline-end: 7px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: var(--border);
  border-radius: var(--radius-pill);
}

.detail-timeline-item { position: relative; }

.detail-timeline-dot {
  position: absolute;
  inset-inline-end: -20px;
  top: 4px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--bg);
  z-index: 1;
}

/* ── .detail-card — THE surface of a detail modal ────────────────────────────
   The timeline card's plain look, promoted to a NAMED central surface so every
   block inside every detail modal wears the same one: session rows, service
   progress, stock/BOM rows, confirmation cards, patient logs. Three modals used
   to hand-roll this inline — that is what made one block look different from
   the one beside it. `.detail-timeline-card` stays as the timeline's alias. */

.detail-card,
.detail-timeline-card {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px 10px;
}

/* A stack of cards inside one section — the spacing lives HERE, not as an
   inline gap re-typed at every call site. */

.detail-card-stack { display: flex; flex-direction: column; gap: var(--space-2); }

/* The card's HEAD: what it is on the inline-start, what it costs on the end. */

.detail-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
}

/* Card CONTENT reads from the inline-START — the counterpart to the receipt
   row, whose value is pinned to the inline-END. Reusing THAT class for a card
   title is exactly what threw the text to the far side (owner-caught):
     receipt row  -> .detail-receipt-label + .detail-receipt-value
     card content -> .detail-card-title + .detail-card-meta + .detail-card-body */

.detail-card-title {
  font-size: var(--text-meta);
  font-weight: 700;
  color: var(--text-primary);
  text-align: start;
  min-width: 0;
  overflow-wrap: anywhere;
}

.detail-card-head > .detail-card-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.detail-card-body {
  font-size: 12.5px;
  color: var(--text-secondary);
  line-height: 1.65;
  text-align: start;
  overflow-wrap: anywhere;
}

.detail-card-meta { font-size: var(--text-xs); color: var(--text-tertiary); margin-top: 2px; }

.detail-card-amount {
  flex-shrink: 0;
  text-align: end;
  font-weight: 700;
  font-size: 12.5px;
}

/* Tone variant for a card that must speak (a warning/confirmation block). Same
   surface, same radius — only the border + wash change, so the family holds. */

.detail-card--warning {
  border-color: color-mix(in srgb, var(--warning) 45%, var(--border));
  background: color-mix(in srgb, var(--warning) 10%, var(--bg-subtle));
}

/* ADR-139 Wave 8 (2026-05-28): drag-handle affordance for the mobile
   bottom-sheet. The pill sits inside the card padding (not on top of
   it) so the header keeps its tap target. Hidden on tablet+ where the
   modal is centered. */

.rm-drag-handle {
  display: block;
  width: 36px;
  height: 4px;
  margin: 8px auto 0;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--text-tertiary) 38%, transparent);
  flex-shrink: 0;
}

@media (min-width: 48em) {
  .rm-drag-handle { display: none; }
}

/* Sizing W9: adopt the central modal-width tokens (tokens.css §12) so every
   ResponsiveModal size derives from one place instead of hardcoded px. */

.rm-card--sm { --rm-max-width: var(--modal-sm); }

.rm-card--md { --rm-max-width: var(--modal-md); }

.rm-card--lg { --rm-max-width: var(--modal-lg); }

.rm-card--xl { --rm-max-width: var(--modal-xl); }

.rm-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--pad-modal-premium-sm) var(--pad-modal-premium);
  /* ADR-139 Wave 11: header tint sits OVER the glass card. Solid
     var(--bg-subtle) would hide the glass — we use a translucent
     wash instead so the blur shows through softly. */
  border-bottom: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
  flex-shrink: 0;
  background: color-mix(in srgb, var(--bg-subtle) 55%, transparent);
}

@media (max-width: 47.99em) {
  .rm-header { padding: 16px 20px; }
}

/* V-Polish 2026-05-27: cluster wraps the optional leading icon with
   the title so they read as a single header element. RTL keeps the
   icon on the right of the text. */

.rm-title-cluster {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  min-width: 0;
  flex: 1 1 auto;
}

.rm-title-icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--radius);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}

.rm-title-icon svg {
  width: 20px;
  height: 20px;
}

/* ADR-139 Wave 2 (2026-05-28): text-stack inside the title cluster wraps
   .rm-title + optional .rm-subtitle so the leading icon stays vertically
   centered against both lines as a single header element. */

.rm-title-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rm-title-text--with-sub .rm-title { line-height: 1.2; }

.rm-title {
  font-size: var(--text-md);
  font-weight: 800;
  color: var(--text-primary);
  line-height: var(--leading-snug);
  /* Allow title text to ellipsis if the modal width gets tight on phones */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ADR-139 Wave 2: optional secondary line below the title. Smaller,
   muted, single-line with ellipsis so it stays compact on phones. */

.rm-subtitle {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-tertiary);
  line-height: 1.3;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
}

/* V-Polish: section divider — wrap a group of related form fields
   with a visible label + thin separator. Tightens the visual scan
   pattern in tall forms (purchase invoice, lab order, expense). */

.modal-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-fluid-xs);
  padding-block-end: var(--space-fluid-sm);
  border-bottom: 1px dashed color-mix(in srgb, var(--border) 70%, transparent);
  margin-block-end: var(--space-fluid-sm);
}

.modal-section:last-child {
  border-bottom: none;
  padding-block-end: 0;
  margin-block-end: 0;
}

.modal-section__title {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: none;
  letter-spacing: 0.01em;
  margin-block-end: 4px;
  /* ADR-139 Wave 8 (2026-05-28): subtle accent dot on the start side
     so each section reads as part of the same visual rhythm as the
     header title-icon circle. The dot is purely decorative; the
     section title remains the primary anchor. */
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.modal-section__title::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 70%, transparent);
  flex-shrink: 0;
}

.modal-section__hint {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  line-height: var(--leading-normal);
}

/* Contextual note bound to ONE field — it explains why that control behaves
   differently right now, unlike .modal-section__hint which describes a whole
   section. Deliberately not a tinted banner: on this white ground a coloured
   box reads as an error, and this is not one. A hairline accent rule on the
   inline-start edge carries the emphasis instead, so the note stays quiet
   until you look at the field it belongs to. Logical properties throughout —
   the rule sits on the correct side in both RTL and LTR. */

.field-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1-5);
  margin-top: 8px;
  padding-inline-start: 8px;
  border-inline-start: 2px solid color-mix(in srgb, var(--accent) 40%, transparent);
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
  color: var(--text-tertiary);
  /* Fade only, no slide. The app allows exactly ONE non-lift translateY
     (the tooltip entrance) and that ratchet is worth more than 3px of
     movement — the hairline rule and the type carry this, not motion. */
  animation: field-note-in 220ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.field-note__icon {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--accent);
  opacity: 0.75;
}

@keyframes field-note-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .field-note { animation: none; }
}

.rm-close {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  font-size: var(--text-base);
  transition: all 160ms ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.rm-close:hover {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

.rm-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--pad-modal-premium);
  min-height: 0;
  /* Keep an at-top pull-down inside the sheet: don't chain the overscroll to
     the page / browser pull-to-refresh. Lets swipe-to-dismiss.js own the
     "drag down from the top" gesture cleanly (passive, no preventDefault). */
  overscroll-behavior: contain;
}

@media (max-width: 47.99em) {
  .rm-body { padding: var(--pad-modal-premium-sm); }
}

.rm-footer {
  display: flex;
  gap: var(--space-2-5);
  padding: var(--pad-modal-premium-sm) var(--pad-modal-premium);
  /* ADR-139 Wave 11: footer also wears a translucent wash over the
     glass card — same recipe as the header for visual symmetry. */
  border-top: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
  flex-shrink: 0;
  background: color-mix(in srgb, var(--bg-subtle) 55%, transparent);
}

.rm-footer .btn { flex: 1 1 auto; }

/* ADR-139 Wave 4 (2026-05-28): structured footer action row.
   When the modal uses primaryAction/secondaryAction/destructiveAction
   props instead of the explicit `footer` ReactNode, we render a flex
   row that lays the destructive button at the start (RTL: right) and
   the secondary+primary pair at the end (RTL: left). Without a
   destructive button the row simply collapses to flex-end. */

.rm-footer--actions {
  justify-content: flex-end;
  flex-wrap: wrap;
}

.rm-footer--actions.rm-footer--has-destructive {
  justify-content: space-between;
}

.rm-footer__end {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  margin-inline-start: auto;
  flex-wrap: wrap;
}

/* Destructive uses outline-with-danger-text styling so it reads as a
   warning without dominating the visual hierarchy of the modal. */

.rm-footer__destructive {
  color: var(--danger);
}

.rm-footer__destructive:hover:not(:disabled) {
  background: var(--danger-soft);
  border-color: var(--danger);
}

.rm-footer--actions .btn {
  /* override the .rm-footer .btn { flex: 1 1 auto } rule so structured
     action buttons size to content (premium pill feel) instead of
     stretching to fill. */
  flex: 0 0 auto;
}

/* ADR-139 Wave 0: input focus glow + label weight + primary CTA hover lift
   are now scoped to .rm-card so every modal gets the premium feel. */

.rm-card .input:focus,
.rm-card .select:focus,
.rm-card textarea:focus {
  border-color: var(--accent);
  box-shadow: var(--input-focus-glow-premium);
  outline: none;
}

.rm-card .input,
.rm-card .select,
.rm-card textarea {
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.rm-card .label {
  font-weight: 700;
  color: var(--text-secondary);
  margin-block-end: 6px;
}

.rm-card .rm-footer .btn.primary {
  transition: transform 140ms ease, box-shadow 160ms ease;
}

/* Hover Wave 2b: the modal primary-button hover that lived here repeated
   the base .btn:hover lift AND the base .btn.primary:hover glow with
   identical values - deleted; the base rules already render it. */

/* ADR-139 Wave 8 (2026-05-28): success-state pulse for the primary
   button. When the consumer's save handler sets aria-busy=true on the
   button while the request is in flight, the button gets a soft
   pulsing glow that hints "something is happening". Pairs with the
   loadingLabel string from the primaryAction primitive. */

.rm-card .rm-footer .btn.primary[aria-busy="true"] {
  animation: rm-btn-pulse 1.2s ease-in-out infinite;
  opacity: 0.92;
}

@keyframes rm-btn-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
  50%      { box-shadow: 0 0 0 8px color-mix(in srgb, var(--accent) 16%, transparent); }
}

/* Reduced motion: skip the pulse but keep the opacity hint. */

@media (prefers-reduced-motion: reduce) {
  .rm-card .rm-footer .btn.primary[aria-busy="true"] {
    animation: none;
  }
}

/* Reduced motion: keep state, drop animation */

@media (prefers-reduced-motion: reduce) {
  .rm-backdrop,
  .rm-card { animation: none !important; }
}

/* ============================================================
   ADR-139 Wave 0 (2026-05-28) — Premium modal shell is the default
   ============================================================
   The styles that used to live under `.rm-card--premium` are now
   baked into the base `.rm-card` above. Every ResponsiveModal
   automatically gets the Ctrl+K-style premium feel.

   `.rm-card--premium` survives as a NO-OP alias so any legacy
   consumer that still passes className="rm-card--premium" keeps
   working without breakage. Eventually we'll grep + remove the
   modifier everywhere, but for now this is the cheapest path.
   ============================================================ */

.rm-card--premium { /* no-op alias: see .rm-card base */ }

/* ============================================================
   ADR-139 Wave 5 (2026-05-28) — Shared form primitives
   ============================================================
   CSS for the primitives in app/src/ui/form-primitives.jsx:
   PatientPicker, InvoiceItemRow, InvoiceSummary, PurchaseItemsEditor.
   These styles read as native parts of the premium modal shell —
   same accent border, same rounded corners, same hover/focus glow.
   (The pay-toggle block was removed with the invoice-status field —
   Anatomy W4, owner 2026-07-16.)
   ============================================================ */

/* ── PurchaseItemsEditor ─────────────────────────────────────── */

.purchase-items-editor {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ── PatientPicker ───────────────────────────────────────────── */

/* ADR-139 A2-v2 (2026-05-28): position: relative so the floating
   dropdown anchors to the picker root. The list itself becomes an
   overlay (position: absolute) so opening it doesn't push the
   surrounding form down — fixing the empty-column gap the owner
   flagged in the post-A2 screenshot. */

.pp {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  position: relative;
}

.pp-input-wrap {
  position: relative;
}

.pp-input-icon {
  position: absolute;
  inset-inline-end: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-tertiary);
  pointer-events: none;
}

.pp-input {
  padding-inline-end: 36px;
}

/* ADR-139 A2-v2: floating overlay dropdown. z-index 20 sits above
   form siblings AND any sticky footer chrome inside the rm-card. */

.pp-list {
  position: absolute;
  top: calc(100% + 6px);
  inset-inline-start: 0;
  inset-inline-end: 0;
  z-index: 20;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-elevated);
  box-shadow: var(--shadow-1, 0 10px 24px rgba(0, 0, 0, 0.12));
  scrollbar-width: thin;
  /* Subtle fade-in so the overlay doesn't pop abruptly. */
  animation: pp-list-in 160ms cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes pp-list-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .pp-list { animation: none; }
}

.pp-empty {
  padding: var(--space-4);
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.pp-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 8px 12px;
  background: transparent;
  border: 0;
  text-align: start;
  cursor: pointer;
  color: var(--text-primary);
  font: inherit;
  transition: background var(--duration-fast) var(--ease-smooth);
}

.pp-item.is-active {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.pp-avatar {
  width: 28px;
  height: 28px;
  font-size: var(--text-meta);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-weight: 700;
}

.pp-item__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.pp-item__name {
  font-weight: 600;
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pp-item__sub {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.pp-selected {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 10px 12px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.pp-selected__icon {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  border-radius: var(--radius-md);
  flex-shrink: 0;
}

.pp-selected__body { flex: 1; min-width: 0; }

.pp-selected__name { font-weight: 700; font-size: var(--text-md); }

.pp-selected__sub { font-size: var(--text-xs); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }

.pp-selected__clear {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--duration-fast) var(--ease-smooth), color var(--duration-fast) var(--ease-smooth);
}

.pp-selected__clear:hover {
  background: var(--danger-soft);
  color: var(--danger);
}

/* ── InvoiceItemRow ──────────────────────────────────────────── */

/* ADR-139 A2-fix (2026-05-28): padding tightened 10/12 -> 8/10
   per owner feedback "حاسه كبير خالص". Saves ~8px per item card. */

.inv-item-row {
  display: grid;
  grid-template-columns: minmax(160px, 1fr) 70px 100px 108px 34px;
  gap: var(--space-2);
  padding: var(--space-3);
  background:
    linear-gradient(145deg,
      color-mix(in srgb, var(--accent) 5%, var(--bg-elevated)),
      var(--bg-elevated) 58%);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--border));
  border-radius: var(--radius-lg);
  align-items: end;
  box-shadow: 0 8px 22px color-mix(in srgb, var(--text-primary) 5%, transparent);
}

.inv-item-row__field { display: flex; flex-direction: column; }

.inv-item-row__field-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.inv-item-row__field-head > span:first-child { min-width: 0; }

.inv-item-row__field-head .reception-modal-field__action { flex: 0 0 auto; }

/* ADR-139 A2-v2: service column capped at 50% so it can't dominate
   the row on wide modals. Was flex: 2 1 200px (no cap), giving the
   select ~600px on size:xl shells — owner flagged as imbalanced
   vs the 90/110/110px numeric columns. */

.inv-item-row__field--service,
.inv-item-row__field--qty,
.inv-item-row__field--price,
.inv-item-row__field--total { min-width: 0; }

.inv-item-row__total {
  font-weight: 800;
  font-size: var(--text-sm);
  color: var(--accent);
  min-height: var(--control-h, 40px);
  padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 24%, var(--border));
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--accent) 9%, var(--bg-elevated));
  text-align: center;
  font-variant-numeric: tabular-nums;
  display: grid;
  place-items: center;
}

.inv-item-row__delete {
  display: flex;
  align-items: flex-end;
  padding-block-end: 2px;
}

.inv-item-row__delete .icon-btn {
  width: 32px;
  height: 40px;
  border: 1px solid color-mix(in srgb, var(--danger) 20%, var(--border));
  background: color-mix(in srgb, var(--danger) 5%, var(--bg-elevated));
}

/* Treatment Engine Wave 2 (ADR-211): tooth picker — full-width row below the
   line fields. Wave 5 R4 (ADR-222) reuses it for the per-line target selector. */

.inv-item-row__teeth {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-block-start: 8px;
  border-block-start: 1px dashed var(--border);
}

@media (max-width: 40em) {
  .inv-item-row {
    grid-template-columns: minmax(0, 1fr) 72px 104px;
  }
  .inv-item-row__field--total { grid-column: 1 / 3; }
  .inv-item-row__delete { grid-column: 3; }
}

@media (max-width: 30em) {
  .inv-item-row {
    grid-template-columns: minmax(0, 1fr) 72px;
  }
  .inv-item-row__field--service { grid-column: 1 / -1; }
  .inv-item-row__field--price,
  .inv-item-row__field--total { grid-column: 1; }
  .inv-item-row__delete { grid-column: 2; }
}

/* Wave 5 R4 (ADR-222): per-line TARGET selector — teeth vs region segmented
   control next to the tooth picker. */

.inv-target-seg { display: flex; flex-wrap: wrap; gap: var(--space-1); }

.inv-target-seg__btn {
  border: 1px solid var(--border); background: var(--bg-subtle);
  color: var(--text-secondary); border-radius: var(--radius-sm); padding: 4px 12px;
  font-size: var(--text-meta); font-weight: 700; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}

.inv-target-seg__btn:hover { border-color: var(--accent); color: var(--accent); }

.inv-target-seg__btn.is-active { background: var(--accent); color: #fff; border-color: var(--accent); }

.tp-wrap { display: flex; flex-direction: column; gap: var(--space-1-5); }

.tp-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.tp-chips { display: flex; gap: var(--space-1); flex-wrap: wrap; }

.tp-chip {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent); border: none; border-radius: var(--radius-sm);
  padding: 2px 8px; font-size: var(--text-meta); font-weight: 800; cursor: pointer;
  font-variant-numeric: tabular-nums;
}

.tp-empty { font-size: var(--text-meta); color: var(--text-tertiary); }

.tp-grid {
  display: flex; flex-direction: column; gap: var(--space-1-5);
  background: var(--bg-elevated, #fff); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: var(--space-2); overflow-x: auto;
}

.tp-row { display: flex; align-items: center; gap: var(--space-1-5); justify-content: center; }

.tp-quad { display: flex; gap: 3px; }

.tp-sep { width: 1px; align-self: stretch; background: var(--border); margin: 0 4px; }

.tp-tooth {
  min-width: 30px; height: 30px; flex: 0 0 auto;
  border: 1px solid var(--border); background: var(--bg-subtle);
  border-radius: var(--radius-sm); font-size: var(--text-meta); font-weight: 700; cursor: pointer;
  color: var(--text-secondary); font-variant-numeric: tabular-nums;
  transition: background .12s, color .12s, border-color .12s;
}

.tp-tooth--on { background: var(--accent); color: #fff; border-color: var(--accent); }

/* Wave 5 E4 (ADR-218): dentition toggle inside the tooth picker. */

.tp-dent { display: flex; gap: var(--space-1); justify-content: center; margin-bottom: 4px; }

.tp-dent-btn {
  border: 1px solid var(--border); background: var(--bg-subtle);
  color: var(--text-secondary); border-radius: var(--radius-sm); padding: 3px 10px;
  font-size: var(--text-meta); font-weight: 700; cursor: pointer;
}

.tp-dent-btn.is-active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* Treatment Engine Wave 3 (ADR-213): dental-chart auto-state tag + the
   per-tooth "رحلة السن" timeline in the detail panel. */

.dc-chip-auto {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px dashed color-mix(in srgb, var(--accent) 55%, transparent);
}

.dc-chip-auto-tag {
  margin-inline-start: 5px; padding: 0 5px; border-radius: 5px;
  font-size: 9px; font-weight: 800; letter-spacing: .2px;
  background: var(--accent); color: #fff; vertical-align: middle;
}

.dc-timeline {
  margin-top: 18px; padding-top: 14px;
  border-top: 1px solid var(--border);
}

.dc-timeline-head {
  display: flex; align-items: center; gap: var(--space-1-5);
  font-size: var(--text-sm); font-weight: 800; color: var(--text-secondary);
  margin-bottom: 10px;
}

.dc-timeline-head svg { color: var(--accent); }

.dc-timeline-item {
  display: flex; gap: var(--space-2); align-items: flex-start;
  padding: 0 0 12px; position: relative;
}

.dc-timeline-item:not(:last-child)::before {
  content: ''; position: absolute;
  inset-inline-start: 4px; top: 12px; bottom: 0;
  width: 2px; background: var(--border);
}

.dc-timeline-dot {
  flex: 0 0 auto; width: 10px; height: 10px; margin-top: 3px;
  border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
  position: relative; z-index: 1;
}

.dc-timeline-body { flex: 1 1 auto; min-width: 0; }

.dc-timeline-r1 {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2);
}

.dc-timeline-proc { font-size: var(--text-sm); font-weight: 800; color: var(--text-primary); }

.dc-timeline-date {
  font-size: var(--text-xs); color: var(--text-tertiary); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.dc-timeline-r2 {
  font-size: var(--text-meta); color: var(--text-tertiary); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.dc-timeline-doc { color: var(--text-secondary); }

.dc-timeline-link {
  display: inline-flex; align-items: center; gap: var(--space-1);
  margin-top: 6px; padding: 3px 8px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent); border: none; cursor: pointer;
  font-size: var(--text-xs); font-weight: 800;
}

.dc-timeline-link:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); }

.dc-timeline-empty {
  font-size: var(--text-meta); color: var(--text-tertiary);
  padding: 4px 0 2px; line-height: 1.5;
}

/* Treatment Engine Wave 4 (ADR-214): invoice-from-chart context banner. */

.nif-tooth-banner {
  display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: 12px; padding: 9px 12px; border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--accent); font-size: var(--text-sm); font-weight: 700; line-height: 1.4;
}

.nif-tooth-banner svg { flex: 0 0 auto; }

/* ── InvoiceSummary ──────────────────────────────────────────── */

/* ADR-139 A2-fix (2026-05-28): compact inline layout.
   3-column grid (subtotal | discount | total) + methods strip
   replaces the previous 5-row vertical stack. ~80px shorter per
   modal. Stacks vertically on phones (<=480px) automatically. */

.inv-summary {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: 14px;
  background:
    radial-gradient(circle at 88% 0,
      color-mix(in srgb, var(--accent) 13%, transparent),
      transparent 44%),
    var(--bg-elevated);
  border: 1px solid color-mix(in srgb, var(--accent) 20%, var(--border));
  border-radius: var(--radius-lg);
  box-shadow: 0 10px 28px color-mix(in srgb, var(--accent) 7%, transparent);
}

.inv-summary__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(120px, .75fr);
  gap: var(--space-3);
  align-items: stretch;
}

.inv-summary__cell {
  display: flex;
  flex-direction: column;
  gap: var(--space-1-5);
  min-width: 0;
}

.inv-summary__cell--total {
  min-height: 78px;
  justify-content: center;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
  border: 1px solid color-mix(in srgb, var(--accent) 24%, var(--border));
}

.inv-summary__cell--discount {
  justify-content: center;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
}

.inv-summary__cell-label {
  font-size: var(--text-xs);
  font-weight: 800;
  color: var(--text-secondary);
  line-height: 1.2;
}

.inv-summary__cell-value {
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.inv-summary__cell-caption {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--danger);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

.inv-summary__cell-caption--before {
  color: var(--text-tertiary);
  text-decoration: line-through;
}

.inv-summary__discount-input {
  width: 100%;
  min-height: 38px;
  padding: 7px 10px;
  font-size: var(--text-sm);
  background: var(--bg-elevated);
}

.inv-summary__total {
  font-size: clamp(22px, 4vw, 28px);
  font-weight: 900;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

/* Stack the 3 columns on phones for legibility */

@media (max-width: 480px) {
  .inv-summary__grid {
    grid-template-columns: 1fr;
    gap: var(--space-2-5);
  }
}

/* Methods strip — row label + pills */

.inv-summary__methods-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-block-start: 12px;
  border-block-start: 1px dashed var(--border);
}

/* View-mode legacy rows (paid / balance) — kept stacked for clarity */

.inv-summary__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.inv-summary__row--view { font-size: var(--text-sm); }

.inv-summary__label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}

.inv-summary__paid {
  font-weight: 700;
  color: var(--success);
  font-variant-numeric: tabular-nums;
}

.inv-summary__balance {
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.inv-summary__balance.is-due { color: var(--danger); }

.inv-summary__row--balance {
  padding-block-start: 8px;
  border-block-start: 1px dashed var(--border);
}

.inv-summary__methods {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-1-5);
  flex: 1 1 300px;
}

.inv-summary__method {
  min-height: 36px;
  padding: 7px 10px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-smooth),
              color var(--duration-fast) var(--ease-smooth),
              border-color var(--duration-fast) var(--ease-smooth);
}

.inv-summary__method:hover {
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
  color: var(--text-primary);
}

.inv-summary__method.is-active {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 32%, var(--border));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent);
}

.inv-summary--controls {
  display: grid;
  grid-template-columns: minmax(118px, .42fr) minmax(0, 1.58fr);
  align-items: stretch;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-elevated);
  box-shadow: none;
}

.inv-summary--controls .inv-summary__cell--discount {
  min-height: 100%;
  padding: 9px 10px;
}

.inv-summary--controls .inv-summary__methods-row {
  align-items: stretch;
  justify-content: center;
  flex-direction: column;
  gap: 7px;
  padding-block-start: 0;
  border-block-start: 0;
}

.inv-summary--controls .inv-summary__methods {
  flex-basis: auto;
  width: 100%;
}

@media (max-width: 36em) {
  .inv-summary--controls {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 30em) {
  .inv-summary__methods { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ============================================================
   ADR-139 Wave 10 (2026-05-28) — QuickAddPatient inline variant
   ============================================================
   Modal variant uses ResponsiveModal's premium shell now. This block
   styles ONLY the inline variant (the embedded sub-form that opens
   inside the WalkInModal when the user clicks "إضافة مريض جديد").
   The slide-in animation curve matches the W1 Ctrl+K curve so the
   inline sub-form lands with the same feel as the parent modal. */

.quickadd-inline-card {
  border-radius: var(--radius-xl);
  background: var(--bg-elevated);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
  overflow: hidden;
  margin-top: 8px;
  box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 10%, transparent);
  animation: quickaddSlideIn 320ms cubic-bezier(0.34, 1.32, 0.64, 1);
}

@keyframes quickaddSlideIn {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .quickadd-inline-card { animation: none; }
}

.quickadd-inline-head {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 14px 18px;
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
}

.quickadd-inline-icon {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  flex-shrink: 0;
}

.quickadd-inline-titles { min-width: 0; flex: 1; }

.quickadd-inline-title {
  font-size: var(--text-md);
  font-weight: 800;
  color: var(--text-primary);
}

.quickadd-inline-sub {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  margin-top: 2px;
}

.quickadd-inline-body { padding: 16px 18px; }

.quickadd-inline-actions {
  display: flex;
  gap: var(--space-2);
  padding: 12px 18px;
  border-top: 1px solid var(--border);
  background: var(--bg-subtle);
  justify-content: flex-end;
}

/* ============================================================
   ADR-139 Wave 6 (2026-05-28) — WizardModal step indicator
   ============================================================
   Renders inside the ResponsiveModal subtitle slot when a wizard
   has 2+ steps. Each step is a pill with a number (or check when
   done), connected by a thin line. Active step uses accent tint;
   completed steps use success tint; future steps muted.
   ============================================================ */

/* ADR-139 A3-v4 (2026-05-28) — wizard step body wrapper.
   The previous global min-height clamp was a one-size-fits-all
   floor that left sparse steps (patient picker only, visit type
   cards only) with a large empty area at the bottom. Owner asked
   for "flexible per step" sizing.
   Default: no min-height — body shrinks to natural content size.
   Per-step override: the consumer can pass a `minHeight` on the
   step config (e.g. step 1: 280px so the overlay PatientPicker
   dropdown stays inside body bounds without triggering rm-body's
   overflow-y: auto). Other steps get true content-driven height. */

.wizard-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ============================================================
   W3 (ADR-077 → redesigned in ADR-079) — Public Landing Page V2
   Premium 2-column hero + stats + how-it-works + features +
   why-us + pricing + FAQ + final CTA + multi-column footer.
   ============================================================ */

.lp-shell{
  --lp-max:1220px;
  --lp-gap:24px;
  --lp-accent:#0ea5b7;
  --lp-accent-dark:#0a8a99;
  --lp-success:#22c55e;
  /* ADR-081: #root is a flex container (display:flex, height:100dvh,
     overflow:hidden) designed for the main app's .main-shell { flex: 1 }
     child. Pre-auth surfaces render directly inside #root, so the
     landing shell must claim the full flex slot AND own its own
     scrolling — otherwise it shrinks to content-width (aligned to start
     = right edge in RTL, leaving the left half empty) and the page
     can't scroll past the first viewport. */
  flex: 1 1 100%;
  width: 100%;
  min-width: 0;
  height: 100dvh;
  overflow-y: auto;
  overflow-x: hidden;
  background:var(--bg);
  color:var(--text-primary);
  font-family:inherit;
}

/* ───── 1. Sticky nav ───── */

.lp-nav{
  position:sticky;top:0;z-index:50;
  background:rgba(255,255,255,.78);
  backdrop-filter:saturate(180%) blur(20px);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  border-bottom:1px solid transparent;
  transition:all 280ms var(--ease-out, ease-out);
}

.lp-nav.is-scrolled{
  background:rgba(255,255,255,.92);
  border-bottom-color:var(--border-subtle, var(--border));
  box-shadow:0 4px 18px rgba(15,23,42,.04);
}

.lp-nav-inner{
  max-width:var(--lp-max);margin:0 auto;
  padding:14px 24px;
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-5);
}

.lp-nav-brand{display:flex;align-items:center;}

/* ADR-083 (revised after owner feedback): the modirify-logo PNG (768×512)
   is a STACKED composition — square "M" icon band (y≈85–225) +
   "Modirify" wordmark text band (y≈234–300) + tagline "Run your clinic.
   Automatically." (y≈310–336) + top/bottom transparent padding. Width
   is set inline via the `width=220` prop on ModirifyLogo (React's
   inline style attribute wins specificity battles vs a CSS rule on the
   base class). Height + cover-crop trim the canvas padding so all three
   content bands sit visible inside the bar (~108 px total height). */

.lp-nav-brand .modirify-logo{
  display:block;
  height:80px !important;
  object-fit:cover;
  object-position:center 41%;
}

.lp-nav-links{
  display:flex;align-items:center;gap:28px;flex:1 1 auto;justify-content:center;
}

.lp-nav-links a{
  font-size:var(--text-base);font-weight:700;color:var(--text-secondary);
  text-decoration:none;cursor:pointer;
  transition:color 160ms;
}

.lp-nav-links a:hover{color:var(--lp-accent);}

.lp-nav-actions{display:flex;gap:var(--space-2-5);align-items:center;}

.lp-nav-login{
  background:transparent;border:none;cursor:pointer;
  font-size:var(--text-base);font-weight:700;color:var(--text-primary);
  padding:8px 14px;border-radius:var(--radius-pill);
  transition:all 160ms;
}

.lp-nav-login:hover{background:var(--bg-subtle);color:var(--lp-accent);}

/* SESSION 3 (ADR-182): country selector in nav — matches lp-nav-login style */

.lp-nav-country-select{
  background:transparent;border:1px solid var(--border);cursor:pointer;
  font-size:var(--text-base);font-weight:700;color:var(--text-primary);
  padding:6px 10px;border-radius:7px;font-family:inherit;
  -webkit-appearance:none;appearance:none;
  transition:all 160ms;
}

.lp-nav-country-select:hover,.lp-nav-country-select:focus{
  background:var(--bg-subtle);color:var(--lp-accent);
  border-color:var(--lp-accent);outline:none;
}

.lp-nav-cta{
  display:inline-flex;align-items:center;gap:var(--space-1-5);
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;border:none;cursor:pointer;
  font-size:var(--text-base);font-weight:800;
  padding:10px 18px;border-radius:var(--radius-pill);
  box-shadow:0 6px 18px rgba(14,165,183,.28);
  transition:all 200ms var(--ease-out, ease-out);
}

.lp-nav-cta:hover{transform:translateY(var(--lift-1));box-shadow:0 10px 24px rgba(14,165,183,.4);}

/* ───── 2. Hero — 2 columns ───── */

.lp-hero{
  position:relative;overflow:hidden;
  padding:90px 24px 110px;
  background:
    radial-gradient(ellipse at 80% 20%, rgba(14,165,183,.08) 0%, transparent 55%),
    radial-gradient(ellipse at 20% 70%, rgba(34,197,94,.06) 0%, transparent 55%);
}

.lp-hero-blob{
  position:absolute;border-radius:50%;filter:blur(80px);
  pointer-events:none;z-index:0;opacity:.55;
  animation:lpFloat 18s ease-in-out infinite;
}

.lp-hero-blob--1{
  width:520px;height:520px;top:-180px;right:-160px;
  background:radial-gradient(circle, var(--lp-accent) 0%, transparent 70%);
  animation-delay:-3s;
}

.lp-hero-blob--2{
  width:420px;height:420px;bottom:-160px;left:-120px;
  background:radial-gradient(circle, var(--lp-success) 0%, transparent 70%);
  animation-delay:-9s;
}

.lp-hero-grid{
  position:absolute;inset:0;pointer-events:none;z-index:0;
  background-image:
    linear-gradient(rgba(14,165,183,.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(14,165,183,.05) 1px, transparent 1px);
  background-size:42px 42px;
  mask-image:radial-gradient(ellipse at center, #000 30%, transparent 70%);
  -webkit-mask-image:radial-gradient(ellipse at center, #000 30%, transparent 70%);
}

.lp-hero-inner{
  position:relative;z-index:1;
  max-width:var(--lp-max);margin:0 auto;
  display:grid;grid-template-columns:1.1fr 1fr;gap:60px;align-items:center;
}

.lp-hero-copy{text-align:start;}

.lp-hero-badge{
  display:inline-flex;align-items:center;gap:var(--space-2);
  padding:8px 16px;border-radius:var(--radius-pill);
  background:rgba(255,255,255,.86);
  backdrop-filter:blur(8px);
  border:1px solid rgba(14,165,183,.32);color:var(--lp-accent);
  font-size:var(--text-meta);font-weight:800;
  margin-bottom:24px;
  animation:lpFadeIn .6s var(--ease-out, ease-out) both;
}

.lp-hero-badge svg{color:#f59e0b;}

.lp-hero-title{
  font-size:clamp(38px, 5.5vw, 62px);
  font-weight:900;line-height:1.1;
  margin:0 0 22px;letter-spacing:-.025em;
  animation:lpFadeIn .7s var(--ease-out, ease-out) .1s both;
}

.lp-hero-accent{
  background:linear-gradient(120deg, var(--lp-accent) 0%, var(--lp-success) 100%);
  background-clip:text;-webkit-background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;
  position:relative;display:inline;
}

.lp-hero-sub{
  font-size:clamp(15px, 1.5vw, 18px);
  color:var(--text-secondary);line-height:1.85;
  max-width:560px;margin:0 0 32px;
  animation:lpFadeIn .8s var(--ease-out, ease-out) .2s both;
}

.lp-hero-actions{
  display:flex;gap:var(--space-3);flex-wrap:wrap;
  margin-bottom:32px;
  animation:lpFadeIn .9s var(--ease-out, ease-out) .3s both;
}

.lp-btn-primary,
.lp-btn-outline{
  display:inline-flex;align-items:center;gap:var(--space-2);
  padding:14px 26px;font-size:var(--text-md);font-weight:800;
  border-radius:var(--radius-pill);cursor:pointer;
  transition:all 220ms var(--ease-out, ease-out);
}

.lp-btn-primary{
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;border:none;
  box-shadow:0 10px 28px rgba(14,165,183,.34);
}

.lp-btn-primary:hover{transform:translateY(var(--lift-2));box-shadow:0 14px 36px rgba(14,165,183,.44);}

.lp-btn-outline{
  background:rgba(255,255,255,.7);
  color:var(--text-primary);
  border:1.5px solid var(--border);
  backdrop-filter:blur(8px);
}

.lp-btn-outline:hover{
  transform:translateY(var(--lift-1));
  border-color:var(--lp-accent);color:var(--lp-accent);
}

.lp-hero-trust{
  display:flex;gap:var(--space-5);flex-wrap:wrap;
  font-size:var(--text-sm);color:var(--text-tertiary);
  animation:lpFadeIn 1s var(--ease-out, ease-out) .4s both;
}

.lp-hero-trust-item{display:inline-flex;align-items:center;gap:var(--space-1-5);font-weight:600;}

.lp-hero-trust-item svg{
  color:#fff;
  background:var(--lp-success);
  border-radius:50%;padding:2px;
  width:18px;height:18px;
}

/* App preview mock (synthetic dashboard) */

.lp-hero-visual{
  position:relative;
  animation:lpFadeIn 1s var(--ease-out, ease-out) .35s both;
}

.lp-mock{position:relative;}

.lp-mock-window{
  background:#fff;
  border:1px solid rgba(15,23,42,.08);
  border-radius:18px;
  overflow:hidden;
  box-shadow:0 30px 60px rgba(15,23,42,.12), 0 12px 28px rgba(15,23,42,.06);
  transform:perspective(1200px) rotateY(-4deg) rotateX(2deg);
  transition:transform 600ms var(--ease-out, ease-out);
}

.lp-mock-window:hover{transform:perspective(1200px) rotateY(-2deg) rotateX(1deg);}

.lp-mock-chrome{
  display:flex;align-items:center;gap:7px;
  padding:11px 14px;
  background:linear-gradient(180deg, #fafbfc 0%, #f5f6f8 100%);
  border-bottom:1px solid rgba(15,23,42,.06);
}

.lp-mock-dot{
  width:11px;height:11px;border-radius:50%;display:inline-block;
}

.lp-mock-dot--red{background:#ed6a5e;}

.lp-mock-dot--yellow{background:#f5bf4f;}

.lp-mock-dot--green{background:#62c554;}

.lp-mock-url{
  flex:1;text-align:center;
  font-size:var(--text-xs);color:var(--text-tertiary);font-weight:600;
  font-family:ui-monospace, monospace;
}

.lp-mock-body{padding:18px;}

.lp-mock-kpis{
  display:grid;grid-template-columns:repeat(3, 1fr);gap:var(--space-2-5);margin-bottom:14px;
}

.lp-mock-kpi{
  padding:var(--space-3);border-radius:10px;
  background:var(--bg-subtle);
  border:1px solid transparent;
}

.lp-mock-kpi--accent{
  background:linear-gradient(135deg, rgba(14,165,183,.1) 0%, rgba(14,165,183,.02) 100%);
  border-color:rgba(14,165,183,.18);
}

.lp-mock-kpi--success{
  background:linear-gradient(135deg, rgba(34,197,94,.1) 0%, rgba(34,197,94,.02) 100%);
  border-color:rgba(34,197,94,.18);
}

.lp-mock-kpi--warning{
  background:linear-gradient(135deg, rgba(245,158,11,.1) 0%, rgba(245,158,11,.02) 100%);
  border-color:rgba(245,158,11,.18);
}

.lp-mock-kpi-label{font-size:var(--text-micro);color:var(--text-tertiary);font-weight:700;}

.lp-mock-kpi-value{
  font-size:var(--text-title);font-weight:900;line-height:1.1;margin:4px 0;
  font-variant-numeric:tabular-nums;
}

.lp-mock-kpi--accent .lp-mock-kpi-value{color:var(--lp-accent);}

.lp-mock-kpi--success .lp-mock-kpi-value{color:var(--lp-success);}

.lp-mock-kpi--warning .lp-mock-kpi-value{color:#f59e0b;}

.lp-mock-kpi-trend{font-size:var(--text-micro);color:var(--text-secondary);font-weight:700;}

.lp-mock-list{
  border:1px solid rgba(15,23,42,.06);
  border-radius:10px;overflow:hidden;
}

.lp-mock-list-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:10px 14px;
  background:var(--bg-subtle);
  font-size:var(--text-meta);font-weight:800;
  border-bottom:1px solid rgba(15,23,42,.06);
}

.lp-mock-pill{
  font-size:var(--text-micro);font-weight:700;
  padding:2px 8px;border-radius:var(--radius-pill);
  background:rgba(14,165,183,.1);color:var(--lp-accent);
}

.lp-mock-row{
  display:grid;grid-template-columns:46px 1fr 1fr auto;gap:var(--space-2-5);align-items:center;
  padding:10px 14px;
  font-size:var(--text-meta);
  border-top:1px solid rgba(15,23,42,.04);
}

.lp-mock-row:first-of-type{border-top:none;}

.lp-mock-row-time{
  font-variant-numeric:tabular-nums;font-weight:800;color:var(--text-primary);
  font-family:ui-monospace, monospace;
}

.lp-mock-row-name{font-weight:700;color:var(--text-primary);}

.lp-mock-row-service{color:var(--text-secondary);font-size:var(--text-xs);}

.lp-mock-row-status{
  font-size:var(--text-micro);font-weight:800;
  padding:2px 8px;border-radius:var(--radius-pill);
}

.lp-mock-row-status.is-confirmed{background:rgba(34,197,94,.14);color:#16a34a;}

.lp-mock-row-status.is-in-progress{background:rgba(14,165,183,.14);color:var(--lp-accent);}

.lp-mock-row-status.is-pending{background:rgba(245,158,11,.14);color:#d97706;}

.lp-mock-float{
  position:absolute;
  background:#fff;
  border:1px solid rgba(15,23,42,.08);
  border-radius:14px;
  padding:12px 14px;
  box-shadow:0 14px 32px rgba(15,23,42,.1);
  animation:lpFloatCard 6s ease-in-out infinite;
}

.lp-mock-float--chart{
  left:-30px;top:46%;
  width:180px;
  animation-delay:-2s;
}

.lp-mock-float--credit{
  right:-12px;bottom:-12px;
  display:flex;align-items:center;gap:var(--space-2-5);
  animation-delay:-4s;
}

.lp-mock-float-label{font-size:var(--text-micro);color:var(--text-tertiary);font-weight:700;}

.lp-mock-float-value{
  font-size:var(--text-md);font-weight:900;color:var(--text-primary);
  font-variant-numeric:tabular-nums;margin-bottom:4px;
}

.lp-mock-sparkline{width:100%;height:34px;}

.lp-mock-float-icon{
  width:32px;height:32px;border-radius:10px;
  display:grid;place-items:center;
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;flex-shrink:0;
}

.lp-mock-float-mini-label{font-size:var(--text-micro);color:var(--text-tertiary);font-weight:700;}

.lp-mock-float-mini-value{font-size:var(--text-meta);font-weight:800;color:var(--text-primary);}

@keyframes lpFadeIn{
  from{opacity:0;transform:translateY(16px);}
  to{opacity:1;transform:translateY(0);}
}

@keyframes lpFloat{
  0%,100%{transform:translate(0,0) scale(1);}
  33%{transform:translate(-22px,18px) scale(1.06);}
  66%{transform:translate(18px,-12px) scale(.96);}
}

@keyframes lpFloatCard{
  0%,100%{transform:translateY(0);}
  50%{transform:translateY(-8px);}
}

/* Reveal-on-scroll */

.lp-reveal{opacity:0;transform:translateY(28px);transition:opacity 700ms var(--ease-out, ease-out), transform 700ms var(--ease-out, ease-out);}

.lp-reveal.is-revealed{opacity:1;transform:translateY(0);}

/* ───── 3. Stats bar ───── */

.lp-stats-section{
  max-width:var(--lp-max);margin:-40px auto 0;padding:0 24px;
  position:relative;z-index:2;
}

.lp-stats-grid{
  display:grid;grid-template-columns:repeat(4, 1fr);gap:0;
  background:#fff;
  border:1px solid var(--border-subtle, var(--border));
  border-radius:18px;
  padding:var(--space-6);
  box-shadow:0 20px 48px rgba(15,23,42,.08);
}

.lp-stat{
  text-align:center;padding:12px 16px;
  border-left:1px solid var(--border-subtle, var(--border));
}

.lp-stat:last-child{border-left:none;}

.lp-stat-value{
  font-size:32px;font-weight:900;line-height:1;
  font-variant-numeric:tabular-nums;
  background:linear-gradient(120deg, var(--lp-accent) 0%, var(--lp-success) 100%);
  background-clip:text;-webkit-background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;
}

.lp-stat-suffix{font-size:18px;}

.lp-stat-label{
  font-size:var(--text-sm);color:var(--text-secondary);font-weight:600;
  margin-top:6px;
}

/* ───── Section shells ───── */

.lp-section{
  max-width:var(--lp-max);margin:0 auto;
  padding:90px 24px;
}

.lp-section-header{
  text-align:center;max-width:720px;margin:0 auto 64px;
}

.lp-section-eyebrow{
  display:inline-block;
  font-size:var(--text-sm);font-weight:800;color:var(--lp-accent);
  text-transform:uppercase;letter-spacing:.1em;
  margin-bottom:14px;
}

.lp-section-title{
  font-size:clamp(28px, 3.8vw, 42px);
  font-weight:900;line-height:1.2;margin:0 0 18px;
  letter-spacing:-.02em;
}

.lp-section-sub{
  font-size:16px;color:var(--text-secondary);
  line-height:1.85;margin:0;
}

/* ───── 4. How it works ───── */

.lp-how-grid{
  display:grid;grid-template-columns:repeat(3, 1fr);gap:var(--space-6);
  position:relative;
}

.lp-how-grid::before{
  content:"";position:absolute;
  top:46px;right:16%;left:16%;
  height:2px;
  background:linear-gradient(to left, transparent 0%, rgba(14,165,183,.18) 20%, rgba(14,165,183,.18) 80%, transparent 100%);
  z-index:0;
}

.lp-how-card{
  position:relative;z-index:1;
  padding:28px 24px;
  background:#fff;
  border:1px solid var(--border-subtle, var(--border));
  border-radius:18px;
  text-align:center;
  transition:all 280ms var(--ease-out, ease-out);
}

.lp-how-card:hover{
  transform:translateY(var(--lift-2));
  border-color:var(--lp-accent);
  box-shadow:0 18px 40px rgba(14,165,183,.12);
}

.lp-how-step{
  position:absolute;top:-16px;left:50%;transform:translateX(-50%);
  width:44px;height:44px;border-radius:50%;
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;
  display:grid;place-items:center;
  font-size:var(--text-base);font-weight:900;
  box-shadow:0 8px 18px rgba(14,165,183,.32);
}

.lp-how-icon{
  width:60px;height:60px;border-radius:var(--radius-lg);
  display:grid;place-items:center;margin:18px auto 16px;
  background:linear-gradient(135deg, rgba(14,165,183,.1) 0%, rgba(34,197,94,.06) 100%);
  color:var(--lp-accent);
}

.lp-how-title{
  font-size:18px;font-weight:800;margin-bottom:10px;
}

.lp-how-body{
  font-size:var(--text-base);color:var(--text-secondary);line-height:1.8;
}

/* ───── 5. Features grid ───── */

.lp-features-grid{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
  gap:var(--space-5);
}

.lp-feature-card{
  padding:28px 22px;
  background:#fff;
  border:1px solid var(--border-subtle, var(--border));
  border-radius:var(--radius-lg);
  transition:all 280ms var(--ease-out, ease-out);
  position:relative;overflow:hidden;
}

.lp-feature-card::before{
  content:"";position:absolute;top:0;right:0;width:100%;height:3px;
  background:linear-gradient(90deg, var(--lp-accent) 0%, var(--lp-success) 100%);
  transform:scaleX(0);transform-origin:right;
  transition:transform 280ms var(--ease-out, ease-out);
}

.lp-feature-card:hover{
  transform:translateY(var(--lift-2));
  border-color:rgba(14,165,183,.4);
  box-shadow:0 14px 36px rgba(14,165,183,.1);
}

.lp-feature-card:hover::before{transform:scaleX(1);}

.lp-feature-icon{
  width:52px;height:52px;border-radius:14px;
  display:grid;place-items:center;
  background:linear-gradient(135deg, rgba(14,165,183,.12) 0%, rgba(14,165,183,.04) 100%);
  color:var(--lp-accent);
  margin-bottom:18px;
  transition:transform 320ms var(--ease-out, ease-out);
}

.lp-feature-card:hover .lp-feature-icon{transform:scale(1.08) rotate(-4deg);}

.lp-feature-title{
  font-size:17px;font-weight:800;margin-bottom:8px;
}

.lp-feature-body{
  font-size:var(--text-base);color:var(--text-secondary);line-height:1.75;
}

/* ───── 6. Why Modirify ───── */

.lp-why-grid{
  display:grid;grid-template-columns:repeat(2, 1fr);gap:var(--space-5);
}

.lp-why-card{
  display:flex;align-items:flex-start;gap:18px;
  padding:var(--space-6);
  background:linear-gradient(135deg, #fff 0%, rgba(14,165,183,.02) 100%);
  border:1px solid var(--border-subtle, var(--border));
  border-radius:var(--radius-lg);
  transition:all 280ms var(--ease-out, ease-out);
}

.lp-why-card:hover{
  border-color:rgba(14,165,183,.32);
  box-shadow:0 12px 32px rgba(14,165,183,.08);
}

.lp-why-icon{
  width:48px;height:48px;border-radius:14px;flex-shrink:0;
  display:grid;place-items:center;
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;
  box-shadow:0 6px 16px rgba(14,165,183,.28);
}

.lp-why-title{font-size:16px;font-weight:800;margin-bottom:6px;}

.lp-why-body{font-size:var(--text-base);color:var(--text-secondary);line-height:1.8;}

/* ───── 7. Pricing grid ───── */

.lp-pricing-grid{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));
  gap:var(--space-6);
  max-width:1000px;margin:0 auto;
}

.lp-pricing-loading{
  text-align:center;padding:var(--space-10);color:var(--text-tertiary);
}

.lp-plan-card{
  position:relative;
  padding:36px 28px;
  background:#fff;
  border:2px solid var(--border-subtle, var(--border));
  border-radius:var(--radius-xl);
  display:flex;flex-direction:column;
  transition:all 280ms var(--ease-out, ease-out);
}

.lp-plan-card:hover{
  transform:translateY(var(--lift-2));
  box-shadow:0 14px 36px rgba(15,23,42,.08);
}

.lp-plan-card.is-highlighted{
  border-color:var(--lp-accent);
  background:linear-gradient(180deg, rgba(14,165,183,.05) 0%, #fff 60%);
  box-shadow:0 14px 36px rgba(14,165,183,.18);
  transform:scale(1.04);
}

.lp-plan-card.is-highlighted:hover{transform:scale(1.04) translateY(var(--lift-2));}

.lp-plan-badge{
  position:absolute;top:-14px;left:50%;transform:translateX(-50%);
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;
  padding:7px 16px;border-radius:var(--radius-pill);
  font-size:var(--text-xs);font-weight:900;letter-spacing:.06em;
  box-shadow:0 6px 16px rgba(14,165,183,.36);
}

.lp-plan-label{
  font-size:var(--text-md);font-weight:800;color:var(--lp-accent);
  letter-spacing:.04em;
  margin-bottom:18px;
}

.lp-plan-price-row{
  display:flex;align-items:baseline;gap:var(--space-1-5);
  margin-bottom:24px;
}

.lp-plan-price{
  font-size:52px;font-weight:900;line-height:1;
  font-variant-numeric:tabular-nums;
  color:var(--text-primary);
  letter-spacing:-.02em;
}

.lp-plan-currency{font-size:18px;font-weight:800;color:var(--text-secondary);}

.lp-plan-period{font-size:var(--text-base);color:var(--text-tertiary);}

.lp-plan-features{
  list-style:none;padding:0;margin:0 0 28px;
  display:flex;flex-direction:column;gap:11px;
}

.lp-plan-features li{
  display:flex;align-items:center;gap:var(--space-2);
  font-size:var(--text-base);color:var(--text-secondary);line-height:1.6;
}

.lp-plan-features li svg{
  color:#fff;background:var(--lp-success);
  border-radius:50%;padding:2px;width:18px;height:18px;
  flex-shrink:0;
}

.lp-plan-cta{
  margin-top:auto;
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  padding:13px 18px;
  border-radius:var(--radius-pill);
  background:var(--bg-card);
  color:var(--text-primary);
  border:1.5px solid var(--border);
  font-size:var(--text-base);font-weight:800;cursor:pointer;
  transition:all 200ms var(--ease-out, ease-out);
}

.lp-plan-cta:hover{
  border-color:var(--lp-accent);color:var(--lp-accent);
  transform:translateY(var(--lift-1));
}

.lp-plan-cta.is-primary{
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 100%);
  color:#fff;border-color:var(--lp-accent);
  box-shadow:0 8px 20px rgba(14,165,183,.28);
}

.lp-plan-cta.is-primary:hover{
  color:#fff;box-shadow:0 12px 28px rgba(14,165,183,.4);
  transform:translateY(var(--lift-1));
}

/* ── Wave 4 (ADR-171): Billing toggle + plan highlights + inherited chip ── */

.lp-billing-toggle-wrap{
  display:flex;justify-content:center;
  margin:0 auto 28px;
  background:var(--bg-subtle,#f8fafc);
  border-radius:var(--radius-pill);
  padding:var(--space-1);
  width:fit-content;
  border:1px solid var(--border);
}

.lp-billing-opt{
  padding:8px 22px;
  border-radius:var(--radius-pill);
  border:none;background:transparent;
  font-size:var(--text-base);font-weight:700;
  color:var(--text-tertiary);
  cursor:pointer;
  display:flex;align-items:center;gap:var(--space-2);
  transition:all 180ms ease;
  line-height:1;
}

.lp-billing-opt.is-active{
  background:#fff;
  color:var(--lp-accent);
  box-shadow:0 2px 8px rgba(15,23,42,.10);
}

.lp-billing-save-chip{
  background:color-mix(in srgb, var(--lp-success,#16a34a) 15%, transparent);
  color:var(--lp-success,#16a34a);
  padding:2px 8px;border-radius:var(--radius-pill);
  font-size:var(--text-xs);font-weight:900;
}

.lp-plan-tagline{
  font-size:var(--text-sm);color:var(--text-secondary);
  margin-top:4px;margin-bottom:10px;
}

.lp-plan-highlights{
  display:flex;gap:var(--space-2-5);flex-wrap:wrap;
  margin-bottom:18px;
}

.lp-plan-highlight{
  display:inline-flex;align-items:center;gap:5px;
  font-size:var(--text-meta);font-weight:700;
  color:var(--text-secondary);
  background:var(--bg-subtle,#f8fafc);
  border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:3px 10px;
}

.lp-plan-highlight svg{color:var(--lp-accent);flex-shrink:0;}

/* Inherited chip — "All {prev} features +" in Pro/Premium cards */

.lp-plan-inherited{
  display:flex!important;align-items:center;gap:7px;
  background:color-mix(in srgb, var(--lp-success,#16a34a) 10%, transparent)!important;
  color:var(--lp-success,#16a34a)!important;
  border-radius:10px;padding:8px 12px!important;
  font-size:13px!important;font-weight:800!important;
  border:1px solid color-mix(in srgb, var(--lp-success,#16a34a) 25%, var(--border));
  margin-bottom:6px;
}

/* Override the generic li svg style for inherited chip so the icon has no circle */

.lp-plan-inherited svg{
  background:none!important;color:var(--lp-success,#16a34a)!important;
  padding:0!important;width:16px!important;height:16px!important;
}

.lp-pricing-note{
  display:flex;align-items:center;gap:var(--space-2);justify-content:center;
  margin-top:36px;
  font-size:var(--text-sm);color:var(--text-tertiary);
  text-align:center;flex-wrap:wrap;
}

/* ───── 8. FAQ ───── */

.lp-faq-list{
  max-width:820px;margin:0 auto;
  display:flex;flex-direction:column;gap:var(--space-2-5);
}

.lp-faq-item{
  background:#fff;
  border:1px solid var(--border-subtle, var(--border));
  border-radius:14px;
  overflow:hidden;
  transition:all 240ms var(--ease-out, ease-out);
}

.lp-faq-item.is-open{
  border-color:var(--lp-accent);
  box-shadow:0 8px 24px rgba(14,165,183,.08);
}

.lp-faq-q{
  width:100%;
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:20px 22px;
  background:transparent;border:none;cursor:pointer;
  font-size:var(--text-md);font-weight:700;color:var(--text-primary);
  text-align:start;
}

.lp-faq-q:hover{background:var(--bg-subtle);}

.lp-faq-q svg{color:var(--text-tertiary);transition:all 240ms;flex-shrink:0;}

.lp-faq-item.is-open .lp-faq-q{color:var(--lp-accent);}

.lp-faq-item.is-open .lp-faq-q svg{color:var(--lp-accent);transform:scale(1.1);}

.lp-faq-a{
  padding:0 22px 22px;
  font-size:var(--text-base);color:var(--text-secondary);line-height:1.95;
  animation:lpFadeIn 280ms var(--ease-out, ease-out) both;
}

/* ───── 9. Final CTA ───── */

.lp-cta-final{padding-top:32px;padding-bottom:96px;}

.lp-cta-card{
  position:relative;overflow:hidden;
  text-align:center;
  padding:72px 32px;
  background:linear-gradient(135deg, var(--lp-accent) 0%, var(--lp-accent-dark) 60%, #064e58 100%);
  border-radius:24px;
  max-width:880px;margin:0 auto;
  color:#fff;
  box-shadow:0 24px 60px rgba(14,165,183,.32);
}

.lp-cta-bg{
  position:absolute;inset:0;pointer-events:none;
  background-image:
    radial-gradient(circle at 18% 30%, rgba(255,255,255,.18) 0%, transparent 35%),
    radial-gradient(circle at 82% 70%, rgba(255,255,255,.12) 0%, transparent 38%);
  animation:lpFloat 14s ease-in-out infinite;
}

.lp-cta-inner{position:relative;z-index:1;}

.lp-cta-eyebrow{
  font-size:var(--text-sm);font-weight:800;color:rgba(255,255,255,.85);
  text-transform:uppercase;letter-spacing:.1em;margin-bottom:14px;
}

.lp-cta-title{
  font-size:clamp(28px, 3.4vw, 38px);
  font-weight:900;line-height:1.25;margin:0 0 16px;
  letter-spacing:-.02em;
}

.lp-cta-sub{
  font-size:16px;color:rgba(255,255,255,.9);line-height:1.85;
  margin:0 auto 32px;max-width:540px;
}

.lp-cta-button{
  display:inline-flex;align-items:center;gap:var(--space-2);
  padding:16px 32px;font-size:16px;font-weight:800;
  background:#fff;color:var(--lp-accent);border:none;
  border-radius:var(--radius-pill);cursor:pointer;
  box-shadow:0 12px 32px rgba(0,0,0,.18);
  transition:all 220ms var(--ease-out, ease-out);
}

.lp-cta-button:hover{
  transform:translateY(var(--lift-2));
  box-shadow:0 18px 40px rgba(0,0,0,.26);
  color:var(--lp-accent-dark);
}

/* ───── 10. Footer ───── */

.lp-footer{
  background:#0f172a;
  color:rgba(255,255,255,.7);
  padding:60px 24px 24px;
}

.lp-footer-inner{
  max-width:var(--lp-max);margin:0 auto;
  display:grid;
  grid-template-columns:2fr 1fr 1fr 1fr;gap:var(--space-10);
}

.lp-footer-col{display:flex;flex-direction:column;gap:var(--space-2-5);}

.lp-footer-col--brand{gap:14px;}

.lp-footer-col--brand .modirify-logo{filter:brightness(0) invert(1);opacity:.95;}

.lp-footer-tagline{
  font-size:var(--text-sm);color:rgba(255,255,255,.6);line-height:1.85;
  margin:0;max-width:340px;
}

.lp-footer-col-title{
  font-size:var(--text-sm);font-weight:800;color:#fff;
  text-transform:uppercase;letter-spacing:.08em;
  margin-bottom:6px;
}

.lp-footer-col a{
  font-size:var(--text-sm);color:rgba(255,255,255,.65);text-decoration:none;
  display:inline-flex;align-items:center;gap:var(--space-1-5);
  transition:color 160ms;cursor:pointer;
}

.lp-footer-col a:hover{color:var(--lp-accent);}

.lp-footer-copy{
  max-width:var(--lp-max);margin:36px auto 0;
  padding-top:24px;
  border-top:1px solid rgba(255,255,255,.1);
  font-size:var(--text-meta);color:rgba(255,255,255,.5);text-align:center;
}

/* ============================================================
   W4 (ADR-078) — Manual receipts: upload form + admin queue
   ============================================================ */

/* ADR-145 F2: legal note under the receipt-upload actions. Small, muted,
   but the billing-policy link is bold so it reads as a real anchor. */

/* SaaS-admin receipts queue */

.sa-receipts-card{padding:18px;display:flex;flex-direction:column;gap:14px;}

.sa-receipts-head{display:flex;align-items:center;justify-content:space-between;}

.sa-receipts-title{
  display:inline-flex;align-items:center;gap:var(--space-2);
  font-size:16px;font-weight:800;color:var(--text-primary);
}

/* Receipts-queue strip family removed (Phase 3 batch B, 2026-07-10):
   the strip rides the ONE Tabs primitive + central .tab-count pill. */

.sa-receipts-list{display:flex;flex-direction:column;gap:var(--space-2-5);}

.sa-receipt-row{
  padding:14px;
  background:var(--bg-card);
  border:1px solid var(--border-subtle, var(--border));
  border-radius:var(--radius-sm);
  display:flex;flex-direction:column;gap:var(--space-2);
}

.sa-receipt-row.is-verified{border-color:#22c55e;}

.sa-receipt-row.is-rejected{border-color:var(--danger);opacity:.85;}

.sa-receipt-row__head{
  display:flex;align-items:center;gap:var(--space-2-5);flex-wrap:wrap;
}

.sa-receipt-row__amount{
  font-size:18px;font-weight:900;color:var(--text-primary);
  font-variant-numeric:tabular-nums;
}

.sa-receipt-row__amount span{font-size:var(--text-sm);color:var(--text-tertiary);margin-right:4px;}

.sa-receipt-row__method{
  font-size:var(--text-meta);color:var(--text-secondary);
  padding:2px 8px;border-radius:var(--radius-pill);
  background:var(--bg-subtle);
}

.sa-receipt-row__meta{
  font-size:var(--text-meta);color:var(--text-tertiary);line-height:1.7;
}

.sa-receipt-row__meta code{
  font-family:ui-monospace, monospace;
  background:var(--bg-subtle);padding:1px 6px;border-radius:var(--radius-xs);
}

.sa-receipt-row__notes,
.sa-receipt-row__rejection{
  display:inline-flex;align-items:center;gap:var(--space-1-5);
  font-size:var(--text-meta);color:var(--text-secondary);
  padding:6px 10px;border-radius:var(--radius-xs, 6px);
  background:var(--bg-subtle);
}

.sa-receipt-row__rejection{background:var(--danger-soft);color:var(--danger);}

.sa-receipt-row__actions{
  display:flex;gap:var(--space-2);flex-wrap:wrap;margin-top:4px;
}

.sa-receipt-row__actions .btn{padding:6px 12px;font-size:var(--text-meta);}

/* Hover Wave 3a: the .btn.danger redefinition + its #b91c1c hover that
   lived here were deleted. The hardcoded hover sat at (0,4,0) and BEAT
   the themed base rule in every theme (midnight/luxe/desert dangers all
   rendered the same fixed red). The base .btn.danger rules render the
   same look in the default theme via color-mix on var(--danger). */

/* Receipt preview overlay */

.sa-receipt-preview-backdrop{
  position:fixed;inset:0;z-index:60;
  background:rgba(0,0,0,.55);
  display:grid;place-items:center;padding:var(--space-5);
}

.sa-receipt-preview__img{
  max-width:100%;max-height:60vh;
  object-fit:contain;border-radius:var(--radius-sm);
  background:var(--bg-subtle);
}

.sa-receipt-preview__pdf{
  width:100%;height:60vh;border:1px solid var(--border);border-radius:var(--radius-sm);
}

.sa-receipt-preview__loading{
  display:inline-flex;align-items:center;gap:var(--space-2);
  padding:var(--space-8);color:var(--text-tertiary);justify-content:center;
}

.sa-receipt-preview__error{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--space-2-5);padding:32px 18px;text-align:center;
  color:var(--danger);background:var(--bg-subtle);
  border-radius:var(--radius);
}

.sa-receipt-preview__error p{margin:0;font-size:var(--text-base);color:var(--text-secondary);}

/* ─── ADR-087: SaaS Admin KPIs + tabs + pricing UI ─── */

/* KPI grid under the hero - 6 tiles, responsive */

/* .sa-kpi / .sa-kpis removed (KpiCard P5, 2026-07-10): the 7 platform KPIs
   live on MetricTile + .kpi-grid (hardcoded hex icon tints retired with it). */

/* Admin tabs-nav family removed (Phase 3 batch A, 2026-07-10): the strip
   rides the ONE ScreenTabBar + the central .tab-count pill now. */

/* Pricing admin tab */

.sa-workspace{
  display:grid;
  gap:var(--space-4);
  min-width:0;
}

.sa-workspace__head{
  display:grid;
  grid-template-columns:minmax(240px, .72fr) minmax(420px, 1.28fr);
  gap:var(--space-4);
  align-items:center;
  padding:18px;
  border:1px solid var(--border);
  border-radius:var(--radius-lg, 16px);
  background:var(--bg-card);
  box-shadow:var(--shadow-sm);
}

.sa-workspace__intro.section-head{
  margin:0;
  min-width:0;
}

.sa-workspace__intro .section-head__lead{min-width:0;}

.sa-workspace__switcher{width:100%;}

.sa-workspace__body{
  display:grid;
  gap:var(--space-4);
  min-width:0;
}

.sa-pricing{display:flex;flex-direction:column;gap:var(--space-4);}

.sa-pricing-entity-switcher{
  width:100%;
  margin-block-end:var(--space-3);
  overflow-x:auto;
  scrollbar-width:none;
}

.sa-pricing-entity-switcher::-webkit-scrollbar{display:none;}

.sa-pricing-entity-switcher .seg-control__item{min-width:max-content;}

.sa-pricing-loading{
  display:flex;align-items:center;justify-content:center;gap:var(--space-2-5);
  padding:var(--space-12);color:var(--text-tertiary);
}

.sa-pricing-publish-status{
  display:flex;align-items:center;gap:12px;padding:13px 15px;
  border:1px solid var(--border);border-radius:14px;background:var(--surface);
}

.sa-pricing-publish-status > div{display:flex;flex:1;flex-direction:column;gap:2px;}

.sa-pricing-publish-status strong{font-size:var(--text-sm);color:var(--text-primary);}

.sa-pricing-publish-status span{font-size:var(--text-meta);color:var(--text-secondary);}

.sa-pricing-publish-status.is-current{border-color:rgba(22,163,74,.32);background:rgba(22,163,74,.06);color:#15803d;}

.sa-pricing-publish-status.is-behind{border-color:rgba(220,38,38,.38);background:rgba(220,38,38,.07);color:#b91c1c;}

.sa-pricing-publish-status.is-unknown{color:var(--text-tertiary);}

.sa-pricing-migration-banner{
  display:flex;align-items:flex-start;gap:14px;
  padding:18px;
  background:rgba(245,158,11,0.08);
  border:1px solid rgba(245,158,11,0.3);
  border-radius:14px;
  color:#92400e;
}

.sa-pricing-migration-banner strong{display:block;margin-bottom:6px;font-size:var(--text-md);}

.sa-pricing-migration-banner p{margin:0;font-size:var(--text-sm);line-height:1.7;}

.sa-pricing-migration-banner code{
  background:rgba(0,0,0,.06);padding:2px 6px;border-radius:var(--radius-xs);
  font-family:ui-monospace,monospace;font-size:var(--text-meta);
}

.sa-pricing-trial{
  display:flex;flex-direction:column;gap:14px;
  padding:18px;
  background:linear-gradient(135deg, rgba(14,165,183,0.05), rgba(59,130,246,0.05));
}

.sa-pricing-trial__head{display:flex;align-items:flex-start;gap:14px;}

.sa-pricing-trial__icon{
  width:44px;height:44px;border-radius:var(--radius);
  background:rgba(14,165,183,0.15);color:#0a8a99;
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
}

.sa-pricing-trial__title{font-weight:900;font-size:16px;color:var(--text-primary);}

.sa-pricing-trial__hint{font-size:var(--text-meta);color:var(--text-secondary);margin-top:3px;line-height:1.6;}

.sa-pricing-trial__row{
  display:flex;align-items:flex-end;gap:var(--space-3);flex-wrap:wrap;
}

.sa-pricing-trial__row .sa-pricing-field{flex:0 0 200px;}

.sa-pricing-section{padding:18px;display:flex;flex-direction:column;gap:14px;}

.sa-pricing-section__head{
  display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-3);
}

.sa-pricing-section__title{
  display:flex;align-items:center;gap:var(--space-2);
  font-size:17px;font-weight:900;color:var(--text-primary);
  margin:0;
}

.sa-pricing-section__hint{
  font-size:var(--text-meta);color:var(--text-secondary);
  margin:4px 0 0;line-height:1.6;
}

.sa-pricing-empty{
  padding:var(--space-6);text-align:center;color:var(--text-tertiary);
  background:var(--bg-subtle);border-radius:var(--radius);
}

/* Bank rails editor (Wave 3 / ADR-108) */

.sa-rails{display:flex;flex-direction:column;gap:14px;}

.sa-rails-row{
  border:1px solid var(--border);border-radius:var(--radius);
  padding:14px;background:var(--bg-subtle);
  display:flex;flex-direction:column;gap:var(--space-3);
}

.sa-rails-row-head{
  display:grid;grid-template-columns:1fr 2fr auto;gap:var(--space-2-5);align-items:end;
}

@media (max-width:640px){.sa-rails-row-head{grid-template-columns:1fr;}}

.sa-rails-numbers{
  display:flex;flex-direction:column;gap:var(--space-2);
  padding-top:10px;border-top:1px dashed var(--border);
}

.sa-rails-number-row{display:flex;align-items:center;gap:var(--space-1-5);}

.sa-rails-footer{display:flex;align-items:center;gap:var(--space-2-5);flex-wrap:wrap;}

.sa-pricing-grid{
  display:grid;grid-template-columns:repeat(auto-fill, minmax(320px, 1fr));
  gap:14px;
}

.sa-pricing-card{
  display:flex;flex-direction:column;gap:14px;
  padding:var(--space-4);
  background:var(--bg-card);
  border:1.5px solid var(--border);
  border-radius:14px;
  transition:border-color 160ms, transform 160ms, box-shadow 160ms;
}

.sa-pricing-card:hover{border-color:var(--accent-soft);}

.sa-pricing-card.has-changes{
  border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(14,165,183,0.08);
}

.sa-pricing-card.is-inactive{
  opacity:0.55;
  background:var(--bg-subtle);
}

.sa-pricing-card__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);
}

.sa-pricing-card__title-row{
  display:flex;align-items:center;gap:var(--space-2);flex:1;min-width:0;
}

.sa-pricing-card__code{
  font-family:ui-monospace,monospace;
  font-size:var(--text-xs);font-weight:700;
  padding:3px 8px;
  background:var(--bg-subtle);color:var(--text-tertiary);
  border-radius:var(--radius-sm);
  flex-shrink:0;
}

.sa-pricing-card__name-input{
  flex:1;
  border:0;background:transparent;
  font-size:16px;font-weight:800;color:var(--text-primary);
  padding:4px 6px;border-radius:var(--radius-sm);
  min-width:0;
}

.sa-pricing-card__name-input:focus{outline:0;background:var(--bg-subtle);}

.sa-pricing-toggle{
  display:inline-flex;align-items:center;gap:var(--space-1-5);
  padding:5px 10px;border:0;background:transparent;
  font-size:var(--text-xs);font-weight:700;cursor:pointer;
  border-radius:var(--radius-pill);
  transition:background 140ms;
}

.sa-pricing-toggle.is-on{color:#16a34a;}

.sa-pricing-toggle.is-off{color:var(--text-tertiary);}

.sa-pricing-toggle:hover{background:var(--bg-subtle);}

/* Migration 080: trial plan card visual distinction */

.sa-pricing-card.is-trial{border-color:var(--lp-accent,#6366f1);background:color-mix(in srgb,var(--lp-accent,#6366f1) 4%,var(--bg));}

.sa-pricing-trial-badge{display:inline-flex;align-items:center;gap:5px;font-size:var(--text-xs);font-weight:700;color:var(--lp-accent,#6366f1);background:color-mix(in srgb,var(--lp-accent,#6366f1) 12%,transparent);padding:4px 10px;border-radius:var(--radius-pill);white-space:nowrap;}

.sa-pricing-card__grid{
  display:grid;grid-template-columns:repeat(3, 1fr);gap:var(--space-2-5);
}

.sa-pricing-card__grid--pack{grid-template-columns:repeat(2, 1fr);}

/* DELIBERATE EXCEPTION to the central field primitive — do not "unify" this.
   (ADR-268/C, owner decision 2026-07-19.)

   The 61 inputs of the pricing editor sit in ~320px cards inside an auto-fill
   grid, so the label stacks ABOVE its input at 11px/700. The central
   .reception-modal-field puts the label BESIDE the input at 12.5px/800 because
   it is built for a roomy single-column modal.

   Swapping this for the central field is not a style tweak: labels grow ~15%
   AND flip from stacked to side-by-side, so inside a 320px card they wrap or
   squeeze the inputs. The two layouts are meaningfully different, and the
   pricing editor is a rarely-opened power tool where density beats uniformity.
   Golden rule: centralize the consistent, specialize the meaningful. */

.sa-pricing-field{display:flex;flex-direction:column;gap:5px;}

.sa-pricing-field__label{
  font-size:var(--text-xs);font-weight:700;color:var(--text-secondary);
}

.sa-pricing-features{
  padding:var(--space-3);background:var(--bg-subtle);border-radius:10px;
}

.sa-pricing-features__label{
  font-size:var(--text-xs);font-weight:700;color:var(--text-secondary);margin-bottom:8px;
}

.sa-pricing-feature{
  display:flex;align-items:center;gap:var(--space-2);
  padding:5px 0;font-size:var(--text-sm);color:var(--text-primary);
  cursor:pointer;
}

.sa-pricing-feature input{cursor:pointer;}

.sa-pricing-feature__mode{
  width:auto;
  min-width:150px;
  margin-inline-start:auto;
  padding-block:5px;
  font-size:var(--text-xs);
}

.sa-pricing-audit-reason{
  display:block;
  margin-top:var(--space-3);
}

.sa-pricing-card__code-input{
  width:120px;
  min-width:0;
  direction:ltr;
  font-family:var(--font-mono, monospace);
}

/* Product-capability catalogue: compact editable chips. This is intentionally
   separate from .sa-pricing-features, which is a vertical checkbox list used
   inside the narrow plan cards. */

.sa-pricing-capability-list{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:8px;
  padding:12px;
  margin-bottom:2px;
  background:var(--bg-subtle);
  border:1px solid var(--border);
  border-radius:12px;
}

.sa-pricing-capability-list.is-retired{
  margin-top:-2px;
  background:transparent;
  border-style:dashed;
}

.sa-pricing-capability-list__label{
  flex:0 0 100%;
  margin-bottom:2px;
  font-size:var(--text-xs);
  font-weight:700;
  color:var(--text-tertiary);
}

.sa-pricing-capability-chip{
  appearance:none;
  display:inline-flex;
  align-items:center;
  gap:6px;
  width:auto;
  min-height:34px;
  padding:7px 11px;
  border:1px solid var(--border);
  border-radius:var(--radius-pill);
  background:var(--bg-card);
  color:var(--text-primary);
  font:inherit;
  font-size:var(--text-sm);
  line-height:1.2;
  box-shadow:0 1px 2px rgba(15,23,42,.04);
  transition:border-color 140ms, background 140ms, box-shadow 140ms;
}

.sa-pricing-capability-chip:hover{
  border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 6%,var(--bg-card));
  box-shadow:0 3px 10px rgba(15,23,42,.08);
}

.sa-pricing-capability-chip:focus-visible{
  outline:3px solid color-mix(in srgb,var(--accent) 22%,transparent);
  outline-offset:2px;
}

.sa-pricing-capability-chip.is-retired{
  min-height:30px;
  color:var(--text-tertiary);
  background:var(--bg-subtle);
  box-shadow:none;
  text-decoration:line-through;
}

.sa-pricing-card__actions{
  display:flex;gap:var(--space-2);
}

/* ADR-088: Clinic quick-actions card + plan-picker / credit-grant modals */

.sa-clinic-actions{
  padding:18px;
  background:linear-gradient(135deg, rgba(14,165,183,0.04), rgba(34,197,94,0.04));
  display:flex;flex-direction:column;gap:var(--space-3);
}

.sa-clinic-actions__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-2-5);}

.sa-clinic-actions__title{
  display:flex;align-items:center;gap:var(--space-2);
  font-size:var(--text-md);font-weight:900;color:var(--text-primary);margin:0;
}

.sa-clinic-actions__hint{
  font-size:var(--text-meta);color:var(--text-secondary);
  margin:4px 0 0;line-height:1.7;
}

.sa-clinic-actions__link{
  background:none;border:0;color:var(--accent);
  font-weight:700;font-size:var(--text-meta);cursor:pointer;
  padding:0 4px;text-decoration:underline;
}

.sa-clinic-actions__link:hover{color:var(--accent-dark, var(--accent));}

.sa-clinic-actions__buttons{display:flex;gap:var(--space-2-5);flex-wrap:wrap;}

.sa-clinics-layout{
  display:grid;
  grid-template-columns:minmax(320px,420px) minmax(0,1fr);
  gap:16px;
  align-items:start;
  min-width:0;
}

.sa-clinic-list-scroll{
  display:grid;
  max-height:calc(100vh - 260px);
  overflow:auto;
}

.sa-clinic-detail-stack{display:grid;gap:16px;min-width:0;}

.sa-modal-empty{padding:20px;text-align:center;color:var(--text-tertiary);}

.sa-modal-backdrop{
  position:fixed;inset:0;z-index:60;
  background:rgba(0,0,0,.55);
  display:grid;place-items:center;padding:var(--space-5);
}

.sa-modal{
  background:var(--bg-card);
  border-radius:var(--radius-lg, 16px);
  padding:22px;
  max-width:min(520px, 96vw);
  max-height:90vh;overflow:auto;
  display:flex;flex-direction:column;gap:14px;
  box-shadow:0 24px 48px rgba(0,0,0,.32);
}

.sa-modal__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-2-5);
  font-size:16px;
}

.sa-modal__hint{
  font-size:var(--text-sm);color:var(--text-secondary);
  margin:0;line-height:1.7;
}

.sa-modal__form{display:flex;flex-direction:column;gap:var(--space-3);}

.sa-plan-picker{display:flex;flex-direction:column;gap:var(--space-2);}

.sa-plan-picker__option{
  display:flex;align-items:center;gap:var(--space-3);
  padding:12px 14px;border:1.5px solid var(--border);border-radius:10px;
  cursor:pointer;transition:border-color 140ms, background 140ms;
}

.sa-plan-picker__option:hover{border-color:var(--accent-soft);}

.sa-plan-picker__option.is-selected{
  border-color:var(--accent);
  background:rgba(14,165,183,0.06);
}

.sa-plan-picker__option input[type="radio"]{flex-shrink:0;cursor:pointer;}

.sa-plan-picker__name{font-weight:800;font-size:var(--text-base);color:var(--text-primary);}

.sa-plan-picker__meta{font-size:var(--text-meta);color:var(--text-secondary);margin-top:3px;}

.sa-capability-editor{display:grid;gap:var(--space-2-5);}

.sa-capability-editor__row{
  display:grid;grid-template-columns:minmax(220px,1fr) minmax(155px,.55fr) auto;
  align-items:center;gap:var(--space-2-5);padding:12px 14px;
  border:1px solid var(--border);border-radius:10px;background:var(--bg-subtle);
}

.sa-capability-editor__copy{min-width:0;}

.sa-capability-editor__name{font-weight:800;font-size:var(--text-base);color:var(--text-primary);}

.sa-capability-editor__meta{font-size:var(--text-meta);color:var(--text-secondary);margin-top:4px;line-height:1.55;}

/* ADR-089 (B-G26): credit ledger view */

.sa-ledger{padding:18px;display:flex;flex-direction:column;gap:var(--space-3);}

.sa-ledger__head{display:flex;align-items:flex-start;justify-content:space-between;}

.sa-ledger__title{
  display:flex;align-items:center;gap:var(--space-2);
  font-size:var(--text-md);font-weight:900;margin:0;
}

.sa-ledger__hint{
  font-size:var(--text-meta);color:var(--text-secondary);margin:4px 0 0;line-height:1.6;
}

.sa-ledger__empty{
  padding:18px;text-align:center;color:var(--text-tertiary);
  background:var(--bg-subtle);border-radius:10px;
}

.sa-ledger__list{
  display:flex;flex-direction:column;gap:var(--space-1-5);
  max-height:420px;overflow:auto;
}

.sa-ledger__row{
  display:grid;
  grid-template-columns:auto 1fr auto auto;
  gap:var(--space-2-5);align-items:center;
  padding:10px 12px;
  border:1px solid var(--border);
  border-radius:var(--radius-md);
  background:var(--bg-card);
  font-size:var(--text-sm);
}

.sa-ledger__row.is-debit{background:rgba(239,68,68,0.04);}

.sa-ledger__row.is-credit{background:rgba(34,197,94,0.04);}

.sa-ledger__amount{font-weight:800;min-width:80px;}

.sa-ledger__row.is-debit .sa-ledger__amount{color:#dc2626;}

.sa-ledger__row.is-credit .sa-ledger__amount{color:#16a34a;}

.sa-ledger__meta{display:flex;align-items:center;gap:var(--space-1-5);flex-wrap:wrap;min-width:0;}

.sa-ledger__chip{
  font-size:var(--text-micro);padding:2px 7px;border-radius:var(--radius-pill);font-weight:700;
}

.sa-ledger__chip.is-monthly{background:rgba(14,165,183,0.12);color:#0a8a99;}

.sa-ledger__chip.is-bonus{background:rgba(34,197,94,0.12);color:#16a34a;}

.sa-ledger__chip.is-mixed{background:rgba(245,158,11,0.12);color:#d97706;}

.sa-ledger__source{
  font-family:ui-monospace,monospace;font-size:var(--text-xs);color:var(--text-tertiary);
}

.sa-ledger__ref{font-size:var(--text-xs);color:var(--text-secondary);}

.sa-ledger__balance{
  font-size:var(--text-xs);color:var(--text-tertiary);
  font-family:ui-monospace,monospace;white-space:nowrap;
}

.sa-ledger__time{
  font-size:var(--text-xs);color:var(--text-tertiary);white-space:nowrap;
}

@media (max-width: 768px){
  .sa-ledger__row{grid-template-columns:1fr;gap:var(--space-1);}
  .sa-ledger__balance,.sa-ledger__time{font-size:var(--text-micro);}
}

/* Responsive (.sa-kpis rules removed with the family, KpiCard P5;
   admin tabs-nav rule removed with its family, Phase 3 batch A) */

@media (max-width: 768px){
  .sa-clinics-layout{grid-template-columns:minmax(0,1fr);}
  .sa-clinics-layout > *{min-width:0;}
  .sa-clinic-list-scroll{max-height:min(52vh,480px);}
  .sa-workspace__head{grid-template-columns:1fr;padding:14px;}
  .sa-workspace__switcher{overflow-x:auto;scrollbar-width:none;}
  .sa-workspace__switcher::-webkit-scrollbar{display:none;}
  .sa-workspace__switcher .seg-control__item{min-width:max-content;}
  .sa-pricing-card__grid{grid-template-columns:1fr 1fr;}
  .sa-pricing-grid{grid-template-columns:1fr;}
  .sa-clinic-actions__buttons .btn{flex:1 1 100%;}
  .sa-capability-editor__row{grid-template-columns:minmax(0,1fr);}
  .sa-capability-editor__row .btn{width:100%;}
}

.sa-receipt-reject__hint{
  font-size:var(--text-sm);color:var(--text-secondary);line-height:1.7;margin:0;
}

@media (max-width: 768px) {
  .sa-receipt-row__head{flex-direction:column;align-items:flex-start;gap:var(--space-1-5);}
  .sa-receipt-row__amount{font-size:16px;}

}

/* W3 (ADR-077) — Back-to-landing pill on the login portal */

.portal-back-link{
  position:fixed;top:18px;right:18px;z-index:30;
  display:inline-flex;align-items:center;gap:var(--space-1-5);
  padding:8px 14px;border-radius:var(--radius-pill);
  background:var(--bg-card);
  border:1px solid var(--border);
  color:var(--text-secondary);
  font-size:var(--text-sm);font-weight:700;cursor:pointer;
  box-shadow:0 4px 12px rgba(0,0,0,.06);
  transition:all 160ms;
}

.portal-back-link:hover{
  background:var(--accent);color:#fff;border-color:var(--accent);
  transform:translateY(var(--lift-1));
}

/* ───── Tablet + phone responsive ───── */

@media (max-width: 1024px) {
  .lp-hero-inner{grid-template-columns:1fr;gap:var(--space-10);text-align:center;}
  .lp-hero-copy{text-align:center;}
  .lp-hero-sub{margin-left:auto;margin-right:auto;}
  .lp-hero-actions,
  .lp-hero-trust{justify-content:center;}
  .lp-hero-visual{max-width:560px;margin:0 auto;}
  .lp-nav-links{display:none;}
  .lp-how-grid{grid-template-columns:1fr;gap:var(--space-8);}
  .lp-how-grid::before{display:none;}
  .lp-why-grid{grid-template-columns:1fr;}
  .lp-footer-inner{grid-template-columns:1fr 1fr;gap:var(--space-8);}
  .lp-footer-col--brand{grid-column:1 / -1;}
}

@media (max-width: 768px) {
  .lp-hero{padding:60px 18px 70px;}
  .lp-section{padding:64px 18px;}
  .lp-section-header{margin-bottom:40px;}
  .lp-nav-inner{padding:12px 16px;}
  .lp-nav-login{display:none;}
  .lp-hero-actions{flex-direction:column;align-items:stretch;width:100%;}
  .lp-btn-primary,.lp-btn-outline{justify-content:center;}
  .lp-stats-section{margin-top:-30px;padding:0 14px;}
  .lp-stats-grid{grid-template-columns:repeat(2, 1fr);padding:18px;}
  .lp-stat{border-left:none;border-bottom:1px solid var(--border-subtle, var(--border));padding:14px 8px;}
  .lp-stat:nth-child(2n){border-left:1px solid var(--border-subtle, var(--border));}
  .lp-stat:nth-last-child(-n+2){border-bottom:none;}
  .lp-stat-value{font-size:26px;}
  .lp-plan-card.is-highlighted{transform:none;}
  .lp-plan-card.is-highlighted:hover{transform:translateY(var(--lift-2));}
  .lp-cta-card{padding:48px 22px;}
  .lp-mock-float--chart{display:none;}
  .lp-mock-float--credit{right:0;bottom:-8px;font-size:var(--text-xs);}
  .lp-footer-inner{grid-template-columns:1fr;gap:var(--space-6);text-align:start;}
  .lp-footer-col--brand{grid-column:auto;}
  .portal-back-link{top:12px;right:12px;font-size:var(--text-meta);padding:6px 12px;}
}

@media (max-width: 480px) {
  .lp-stats-grid{grid-template-columns:1fr;}
  .lp-stat{border-left:none;border-bottom:1px solid var(--border-subtle, var(--border));}
  .lp-stat:nth-child(2n){border-left:none;}
  .lp-stat:last-child{border-bottom:none;}
}

/* ============================================================
   V-Reception/W1 — Premium kanban polish + sub-states
   ============================================================
   Goals:
   • Full-width kanban (no Schedule panel right sibling anymore).
   • WhatsApp pre-confirm card treatment (green wash + pulse).
   • Waiting for بنج / أشعة card treatment (orange wash + pulse).
   • Secondary actions revealed on hover (primary always visible).
   • Smoother stage colors at the column header level.
   ============================================================ */

/* Full-width kanban: 6 equal columns share the row (cancelled joined the
   board 2026-07-18 — owner). We lock the constraint explicitly here for the
   Reception layout rather than relying on the base rule. */

.kanban-board.kanban-full-width {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
}

/* Card padding moved off the JSX inline style (2026-07-18). Inline beats every
   media query, so the breakpoint tiers below could only reach it with
   !important; owning it here lets each tier just declare what it needs. This
   is the phone/tablet value — >=769px tightens it where columns compete. */

.kanban-board.kanban-full-width .kanban-card {
  padding: 14px;
}

.kanban-board.kanban-full-width .kanban-col {
  max-width: none;
  min-width: 0;
}

.kanban-board.kanban-full-width .kanban-cards-stack {
  min-height: 240px;
}

/* Column header — premium gradient stripe per stage, picked up via
   data-stage attribute. The currentColor (set inline by the stage
   color) drives the ::before stripe on .kanban-col-hdr. */

.kanban-col[data-stage="pending"]     .kanban-col-hdr { color: var(--warning); }

.kanban-col[data-stage="confirmed"]   .kanban-col-hdr { color: var(--info); }

.kanban-col[data-stage="in-progress"] .kanban-col-hdr { color: var(--accent); }

.kanban-col[data-stage="done"]        .kanban-col-hdr { color: var(--success); }

.kanban-col[data-stage="no_show"]   .kanban-col-hdr { color: var(--danger); }

.kanban-col[data-stage="cancelled"]   .kanban-col-hdr { color: var(--danger); }

/* Card hover lift — slightly stronger than base for premium feel.
   Uses the --lift-2 token so the U-10/4 smoke check (translateY(-Npx)
   limited to non-lift sites) stays at 2 occurrences. */

.kanban-card {
  position: relative;
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

/* Hover Wave 2b: THE kanban-card hover (was three stacked rules; these
   are the values the cascade already produced - transform + shadow from
   this rule, the border tint from the premium rule).
   Hover Wave 3c (owner, 2026-07-11): deliberately NOT touch-guarded - on
   phones the tap-emulated hover is the interaction: it lifts the card AND
   reveals the secondary action row (press-to-show, like the desktop). The
   3b guard made tapped cards feel dead, so it was lifted here only. */

.kanban-card:hover {
  transform: translateY(var(--lift-2));
  box-shadow:
    0 6px 16px rgba(0, 0, 0, 0.08),
    0 2px 4px rgba(0, 0, 0, 0.04);
  border-color: color-mix(in srgb, var(--accent) 25%, var(--border));
}

/* Primary action row — always visible. */

.kanban-card-primary-row {
  display: flex;
  gap: var(--space-1-5);
  flex-wrap: wrap;
  align-items: center;
}

/* Pre-visit context: trigger-maintained patient balance + central medical strip. */

.kanban-patient-context{
  display: grid;
  gap: var(--space-1-5);
  margin-bottom: 8px;
}

.kanban-balance-notice{
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  padding: 7px 9px;
  border: 1px solid color-mix(in srgb, var(--danger) 22%, var(--border));
  border-radius: var(--radius-sm);
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--danger) 7%, var(--bg-elevated)),
    color-mix(in srgb, var(--warning) 4%, var(--bg-elevated)));
}

.kanban-balance-notice__icon{
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  color: var(--danger);
  border-radius: 50%;
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

.kanban-balance-notice__label{
  flex: 1;
  min-width: 0;
  color: var(--text-secondary);
  font-size: 10.5px;
  font-weight: 800;
}

.kanban-balance-notice > div{ flex: 0 0 auto; }

/* Completed-visit summary: the started/ended visual timeline only. The result
   chips (duration + paid) moved onto the ONE .kanban-badge family at the card
   top (owner 2026-07-18 #3) - their private meta-row CSS died with them. */

.kanban-visit-summary{
  position: relative;
  margin-bottom: 8px;
  padding: 9px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--success) 22%, var(--border));
  border-radius: var(--radius-sm);
  background:
    radial-gradient(circle at 100% 0%, color-mix(in srgb, var(--success) 10%, transparent), transparent 48%),
    color-mix(in srgb, var(--success) 3%, var(--bg-elevated));
}

.kanban-visit-summary__times{
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
}

.kanban-visit-summary__times.has-range{
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.kanban-visit-summary__times.has-range::before{
  content: '';
  position: absolute;
  z-index: 0;
  inset-inline: 25px;
  top: 13px;
  border-top: 1px dashed color-mix(in srgb, var(--success) 32%, var(--border));
}

.kanban-visit-time{
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  min-width: 0;
}

.kanban-visit-time__marker{
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  color: var(--success);
  border: 1px solid color-mix(in srgb, var(--success) 28%, var(--border));
  border-radius: 50%;
  background: color-mix(in srgb, var(--success) 11%, var(--bg-elevated));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bg-elevated) 90%, transparent);
}

.kanban-visit-time.is-end .kanban-visit-time__marker{
  color: var(--bg-elevated);
  border-color: var(--success);
  background: var(--success);
}

.kanban-visit-time__copy{
  display: grid;
  gap: 1px;
  min-width: 0;
  line-height: 1.15;
}

.kanban-visit-time__label{
  color: var(--text-tertiary);
  font-size: 9px;
  font-weight: 700;
}

.kanban-visit-time__copy strong{
  color: var(--text-primary);
  font-size: 11.5px;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
}

/* ── ONE kanban card FIELD chrome (owner 2026-07-18) ──────────────
   The doctor select, the chair select and the read-only doctor on a settled
   card carried three copies of the same inline style, so the "done" card's
   doctor rendered as loose text while every other column showed a box. One
   class now; `--static` only removes the affordances, never the geometry. */

.kanban-field-row {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  margin-bottom: 8px;
}

.kanban-field-ico { font-size: var(--text-meta); flex: 0 0 auto; }

.kanban-field {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  /* The floor BOTH branches share, so the settled card's read is the same box
     as the editable select instead of a shorter one (owner 2026-07-18: the
     done column's doctor sat lower and smaller than every other column's).
     28px is the select's own natural height - matched, not fought. */
  min-height: 28px;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1.5;
  padding: 2px 6px;
  border-radius: var(--radius-sm, 6px);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-primary);
  cursor: pointer;
}

.kanban-field--static {
  cursor: default;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* On phones a global tap-target rule gives every `select` min-height 44px, but
   the settled card's doctor is a DIV - so it stayed at 22px and the done column
   read visibly shorter than the other five (owner screenshot, iPhone 430px).
   The floor is restated on the CLASS, not on `--static`: `.kanban-field`
   (0,1,0) outranks the global bare `select` (0,0,1) even from outside a media
   query - specificity ignores media - so scoping it to the static branch would
   SHRINK the control to 28px instead of raising the read, flipping the same
   mismatch around. Measured in the live page before shipping. */

@media (max-width: 768px) {
  .kanban-field { min-height: 44px; }
}

/* ── ONE kanban row-action button (owner 2026-07-18: «خليها ايقونات كلها») ──
   Every card option in every column is this icon button; the verb lives on
   the tooltip + aria-label. Tone is the only variable. The `.btn.` prefix
   matches `.btn.ghost`'s specificity so the tone colour actually lands. */

.kanban-row-action {
  padding: var(--space-1);
  min-width: 28px;
  justify-content: center;
}

.btn.kanban-row-action { color: var(--text-tertiary); }

.btn.kanban-row-action--success { color: var(--success); }

.btn.kanban-row-action--warning { color: var(--warning); }

.btn.kanban-row-action--danger  { color: var(--danger); }

/* Secondary action row — hidden until hover, then fades in.
   On touch devices (hover: none) we keep them always-visible so
   tap-only users can reach the actions. */

.kanban-card-secondary-row {
  display: flex;
  gap: var(--space-1-5);
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent);
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transform: translateY(-4px);
  transition:
    opacity var(--duration-base) var(--ease-smooth),
    max-height var(--duration-base) var(--ease-smooth),
    transform var(--duration-base) var(--ease-smooth);
  pointer-events: none;
}

.kanban-card:hover .kanban-card-secondary-row,
.kanban-card:focus-within .kanban-card-secondary-row {
  opacity: 1;
  /* Owner 2026-07-18: with the confirm button the pending card has FOUR
     actions; overflow:hidden + a 40px cap silently clipped the last one
     (ملف المريض). Wrap + room for two lines - nothing is ever hidden. */
  flex-wrap: wrap;
  max-height: 76px;
  transform: translateY(0);
  pointer-events: auto;
}

/* Hover Wave 4a (owner, 2026-07-11): on touch the kanban card's actions
   live in the central QuickActionsSheet (a tap on the card opens it -
   the same bottom-sheet primitive billing/accounting/inventory use), so
   the inline secondary row is DESKTOP-ONLY. Hiding it on coarse pointers
   follows the sheet convention: the sheet carries the row's actions, the
   screen hides its inline mobile buttons (no duplication). The 3c
   always-visible block died earlier for crowding every phone card. */

@media (hover: none) {
  .kanban-card-secondary-row { display: none; }
}

/* ── WhatsApp pre-confirmed: green wash + pulse halo ─────────────── */

.kanban-card.wa-pre-confirmed {
  background:
    linear-gradient(
      135deg,
      color-mix(in srgb, var(--success) 10%, var(--bg-elevated)) 0%,
      color-mix(in srgb, var(--success) 4%, var(--bg-elevated)) 100%
    );
  border-color: color-mix(in srgb, var(--success) 35%, var(--border));
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--success) 20%, transparent),
    0 2px 6px color-mix(in srgb, var(--success) 12%, transparent);
  animation: wa-pre-confirm-pulse 2.6s ease-in-out infinite;
}

@keyframes wa-pre-confirm-pulse {
  0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--success) 20%, transparent), 0 2px 6px color-mix(in srgb, var(--success) 12%, transparent); }
  50%      { box-shadow: 0 0 0 1px color-mix(in srgb, var(--success) 45%, transparent), 0 4px 14px color-mix(in srgb, var(--success) 22%, transparent); }
}

/* Persistent badge for WA pre-confirmed (survives the 8s recentAction window) */

/* ── Waiting for بنج / أشعة: orange wash + pulse ─────────────────── */

.kanban-card.waiting-extra {
  background:
    linear-gradient(
      135deg,
      color-mix(in srgb, #ea580c 12%, var(--bg-elevated)) 0%,
      color-mix(in srgb, #ea580c 4%, var(--bg-elevated)) 100%
    );
  border-color: color-mix(in srgb, #ea580c 38%, var(--border));
  box-shadow:
    0 0 0 1px color-mix(in srgb, #ea580c 22%, transparent),
    0 2px 6px color-mix(in srgb, #ea580c 14%, transparent);
  animation: waiting-pulse 2.4s ease-in-out infinite;
}

@keyframes waiting-pulse {
  0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, #ea580c 22%, transparent), 0 2px 6px color-mix(in srgb, #ea580c 14%, transparent); }
  50%      { box-shadow: 0 0 0 1px color-mix(in srgb, #ea580c 48%, transparent), 0 4px 14px color-mix(in srgb, #ea580c 26%, transparent); }
}

/* Reduce motion — respect the system preference. The retired
   waiting-badge-bob keyframes died with the old badge family; the ONE
   .kanban-badge slide-in respects the preference here instead. */

@media (prefers-reduced-motion: reduce) {
  .kanban-card.wa-pre-confirmed,
  .kanban-card.waiting-extra,
  .kanban-badge {
    animation: none;
  }
}

/* Stage-column max-width override for the full-width layout.
   The base rule (line ~1681) caps columns at 220px — that was right
   for the 60% split layout but limits the new full-width kanban. */

.kanban-board.kanban-full-width .kanban-col {
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-subtle) 60%, var(--bg-elevated));
}

/* ADR-139 A4 (2026-05-28): AppointmentSideDrawer migrated to
   ResponsiveModal. The bespoke .side-drawer-* namespace (backdrop,
   slide-in panel, custom header/body/footer rhythm, RTL slide
   keyframes) is fully retired - the modal inherits the premium
   glass shell + Wave 4 action footer from the shared primitive. */

/* ============================================================
   V-Appointments/W2 — Day-view drag-drop visual feedback
   ============================================================
   • .appt-block.is-draggable        — grab cursor on hover
   • .appt-block.is-dragging         — dim + slight scale-down
   • .appt-slot.drop-target          — accent dashed ring (valid drop)
   • .appt-slot.drop-forbidden       — danger dashed ring (occupied)
   ============================================================ */

@keyframes appt-slot-drop-pulse {
  0%, 100% { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
  50%      { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 75%, transparent); }
}

/* ============================================================
   Hover Wave 1c (2026-07-11) — appointments hover: JS -> CSS
   ============================================================
   Pixel-identical port of the four hand-rolled JS hover sites in
   appointments.jsx (mouse handlers + a `hovered` useState). The
   drop-target/.drop-forbidden rules above keep winning via their
   !important backgrounds, exactly as they beat the old inline style.
   - .appt-slot.is-current: the "now" row tint (was an inline bg)
   - .appt-slot.is-empty:hover: empty-cell invite; (0,3,0) beats
     .is-current while hovered, matching the old JS behavior
   - .appt-block hover: scale + reveal edit/cancel/more actions
   - .appt-status-menu rows point at the central --state-* tokens
   - .appt-pill-week: week-view pill shadow */

/* Touch devices: hide drag affordance (HTML5 dnd is desktop-only here). */

/* ============================================================
   V-Phase2 — Mobile Polish (ADR-132)
   ============================================================
   Owner audit on 2026-05-27 with Preview MCP screenshots
   (viewport 375x812) flagged six issues across the Reception +
   Appointments screens; A and B are shared chrome and affect
   every screen via the shell siblings, so the fix belongs here
   in the global stylesheet rather than per-screen.

   Issues addressed:
   A. TopBar at 197px on phones (3 rows). The 132px-min clock
      div was the single biggest contributor — it forced the
      .topbar-actions row to wrap once on the clock + WhatsApp
      icons, then again when the avatar wouldn't fit.
   B. Hero card duplicates topbar info (clock + greeting + clinic
      name) and adds ~250px more chrome. Compress on phones.
   C. Kanban broken: .kanban-board.kanban-full-width (specificity
      0,2,0) overrode Wave 5b's grid-template-columns: 1fr rule
      at <=480px (specificity 0,1,1), so the 5-col 180px-each
      horizontal scroll layout shipped to phones unchanged.
   D. Day calendar enforced Math.max(600, ...) minWidth even at
      1 room, clipping "الغرفة" to "الغر" on a 375px viewport.
      Fixed in appointments.jsx; this block handles font/cell
      compression so the room label has breathing room.
   E. KPI strip on Reception used .l-grid--sm (auto-fit, 10rem
      min) which resolved to 1 column on phones — 5 tiles tall
      ~400px of chrome before the kanban. New 2x2+1 layout.
   F. Week view 7-col grid: Wave 5c's stack rule existed but
      inline grid-template-columns wasn't being beaten reliably.
      Belt-and-suspenders enforcement here.

   Class hooks added to JSX in this same commit:
     topbar-subtitle  — app/shell/topbar.jsx
     appt-day-grid    — app/screens/appointments.jsx
     appt-day-scroll  — app/screens/appointments.jsx
     reception-kpis   — app/screens/reception.jsx
*/

@media (max-width: 768px) {
  /* A — TopBar: single-row chrome.
     The earlier @media (max-width: 768px) rule at line ~3202
     applies flex-wrap: wrap + sets .search and .topbar-actions
     to width: 100% — that produces the 3-row pile. Override
     here: nowrap, hide search (cmdk-launch icon already gives
     a search affordance), hide subtitle (tabs strip + hero
     already restate any context the user needs on a phone). */
  .topbar {
    flex-wrap: nowrap;
    gap: var(--space-1-5);
    padding: 10px 14px;
  }
  /* On mobile the wide launcher would eat the screen title, so it collapses to
     a magnifier (.topbar-search-toggle) that opens the SAME command palette -
     which sizes itself to the screen there. ADR-287 removed the old
     expand-in-place overlay bar: a second input on a phone is a second place
     to type the same query. */
  .topbar > .search-launcher { display: none; }
  .topbar-search-toggle { display: inline-flex !important; }
  .topbar > .topbar-actions {
    order: 2;
    width: auto;
    flex-wrap: nowrap;
    flex-shrink: 0;
    margin-inline-start: auto;
    gap: var(--space-1);
    justify-content: flex-end;
  }
  /* !important — the subtitle is rendered inline by Topbar.jsx, so the
     hide needs !important to beat the inline style. */
  .topbar-title-block .topbar-subtitle { display: none !important; }
  /* Chrome round 3 (owner 2026-08-13): the theme control is BACK on phones.
     It used to be hidden here and re-implemented as a collapsible swatch block
     in the nav sheet; the owner asked for it in the bar instead. It costs the
     bar nothing, because the language globe it replaces was removed in the
     same change and appearance+language are now one popover. */
}

@media (max-width: 480px) {
  /* B — (RETIRED R3 Phase 3, 2026-07-11) the .dash-hero phone-compression rules
     are gone with the shell. The one bespoke hero left, the clinical .pf-hero
     header, self-compresses via its own ≤768 / ≤480 rules. */

  /* C — Kanban: shared rules for every mobile breakpoint. The
     specificity fix (.kanban-board.kanban-full-width beats line
     ~12959) lives here; the per-breakpoint grid templates live in
     the V-Phase2 Wave 2 block below. !important is required for
     the grid-template-columns because the existing
     `.kanban-board.kanban-full-width` rule (specificity 0,2,0)
     would otherwise lock the column count to 5. */
  .kanban-board.kanban-full-width .kanban-col {
    min-width: 0;
  }
  /* Sticky stage header so the user keeps context while scrolling
     a long column of cards. Applies at every mobile breakpoint
     (chips mode, 2-col, 3-col). */
  .kanban-board.kanban-full-width .kanban-col-hdr {
    position: sticky;
    top: 0;
    z-index: 2;
  }

  /* D — Day calendar: with the JSX-side fix removing the 600
     minWidth floor for one-room layouts, the grid fits in 343px
     content width naturally. This rule trims the time column to
     60px so even ultra-narrow viewports have room for room
     labels. */
    
  /* E — Reception KPIs: 2 columns; the 5th tile spans the row
     via :last-child:nth-child(odd) so the layout stays balanced
     (2 + 2 + 1 wide rather than 2 + 2 + 1 narrow). */
  .reception-kpis.l-grid {
    grid-template-columns: 1fr 1fr !important;
    gap: var(--space-2);
  }
  .reception-kpis.l-grid > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* F — Week view defensive stack. Wave 5c had the right rule;
     this is belt-and-suspenders. Each day card in the stack
     gets a left-aligned header so the user can scan the stream
     without losing day context. */
      }

/* ============================================================
   V-Phase2 Wave 2 — Adaptive kanban (ADR-132 follow-up)
   ============================================================
   Owner picked Option D (adaptive layout per breakpoint) after the
   single-column stack on Wave 1 felt too tall — ~1300px of scroll
   to walk the full 5 stages, with each empty stage eating a 250px
   card vertically. New behavior:

     <=600px  chips filter + 1 stage at a time (full-width cards)
              — covers EVERY phone (Nav-Kanban 2026-07-05: raised from
              <=390, which left 400-460px flagships — iPhone Pro Max,
              Pixel — in a cramped 2-col instead of the premium chip view)
     601-768  2 cols: محجوز+انتظار / طبيب+تمت / لم يحضر spans
     769-1023 3 cols: 3 active stages row 1 (span 2 each on a 6-fr
              grid) + 2 reference stages row 2 (span 3 each)
     >=1024   5 cols — current desktop, unchanged

   Default chip selection is "pending" (محجوز) because that's the
   most common entry point for receptionist workflow (new patient
   arrives, gets booked into pending, then walks in to confirmed).
   The chip strip is always rendered in JSX so the active state
   lives in one place; CSS hides it at >=601px since the parallel
   layouts make the chips redundant.

   Nav-Kanban v2 (ADR-238, 2026-07-09) — Reels carousel: the <=600px
   tier stopped hiding the 4 non-active columns (the old
   data-active-stage display:none filter) and now lays ALL 5 columns
   side by side inside .kanban-scroll-strip — a native horizontal
   scroll-snap carousel. Why: swipe existed (tab-swipe.js stage-swipe)
   but nothing FELT swipeable — mid-drag the active column slid over a
   void (neighbours were display:none), there was no resting peek, and
   no first-run affordance. Native panning gives the stories physics
   for free (both columns glued to the finger + momentum + rubber-band)
   and the 87% column width leaves a live sliver of the neighbour
   column on each side — the permanent "there is more" cue.
   tab-swipe.js yields automatically (startedInHScroller detects the
   strip), so the old modirify:stage-swipe listener in reception.jsx
   stays as a harmless no-carousel fallback, not a fight.

   Drag-drop:
     - At >=601px every column is visible, dnd works as before.
     - At <=600px drop targets are extended to the chip buttons
       (drag a card onto another stage's chip), and the strip gets
       .drag-frozen (overflow-x hidden) while a card is dragged so
       the carousel never pans mid-drag (see onDrop in reception.jsx).
*/

/* Dots row hidden by default (desktop + tablet + 2-col range).
   ADR-238 R2: the chip strip was retired (owner phone QA — the
   state-driven chips visibly lagged the native pan); the stories
   dots row replaced it. */

.kanban-dots { display: none; }

/* Layout-transparent wrapper: on >600px the strip has no box
   (display:contents) so the 2/3/5-col grid tiers keep treating the
   stage columns as grid items of the board. This is also why the
   tier selectors below use DESCENDANT (not >) combinators. */

.kanban-scroll-strip { display: contents; }

@media (max-width: 600px) {
  /* Board becomes a plain block: chips row on top, carousel below.
     (grid-template-columns from the base rule is inert on block.) */
  .kanban-board.kanban-full-width {
    display: block !important;
    overflow-x: visible !important;
    padding-bottom: 0 !important;
  }
  /* The carousel itself: all 5 stage columns in one native scroller.
     scroll-snap centers a column per "page"; the 87% flex-basis is
     the peek knob (~6.5% of the neighbour visible on each side).
     overscroll-behavior-x keeps the edge rubber-band inside the strip
     (no browser back-gesture / page overscroll chaining). */
  .kanban-scroll-strip {
    display: flex;
    gap: var(--space-2-5);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 2px 2px 6px;
    /* ADR-238 R2: each column keeps its OWN height — a tall column
       (many cards) no longer stretches its empty siblings to match
       (flex default is stretch; owner phone QA flagged it). */
    align-items: flex-start;
  }
  .kanban-scroll-strip::-webkit-scrollbar { display: none; }
  /* A card being dragged freezes the pan so drag never fights swipe
     (scroll offset is preserved across the overflow flip). */
  .kanban-scroll-strip.drag-frozen { overflow-x: hidden; }
  /* ADR-238 R3: drag mode fattens the drop slivers — columns narrow
     80% → 74% so BOTH neighbours show ~13% live edges (drop a card
     straight onto a neighbour's sliver to move it), and every column
     advertises droppability with a dashed accent inset. The hovered
     column keeps the strong .drop-target treatment; the JSX re-centers
     the active column across the width flip, and holding the card over
     a sliver ~600ms slides the carousel one step (dwell-to-advance). */
  .kanban-scroll-strip.drag-frozen .kanban-col {
    flex-basis: 74%;
  }
  .kanban-scroll-strip.drag-frozen .kanban-col:not(.drop-target) {
    outline: 2px dashed color-mix(in srgb, var(--accent) 40%, transparent);
    outline-offset: -2px;
  }
  .kanban-scroll-strip .kanban-col {
    /* R2: 87% → 80% (owner phone QA) — a fatter neighbour peek
       (~10% each side) reads more clearly as "swipe me". */
    flex: 0 0 80%;
    min-width: 0;
    scroll-snap-align: center;
    display: flex;
    flex-direction: column;
  }
  /* R2: compact empty columns — the desktop 240px stack floor made an
     empty stage a tall white slab on phones. 120px keeps a visible
     drop/empty area without dominating the viewport. */
  .kanban-board.kanban-full-width .kanban-cards-stack {
    min-height: 120px;
  }
  /* ADR-238 R2: stories-style position dots (replaced the chip strip).
     The BUTTON is the touch target (30x28px); the visible dot is the
     ::after pseudo — idle 7px dot, active 22px pill tinted with the
     stage color (--dot-c set inline per dot). The active class is
     painted directly from the scroll stream (zero lag). */
  .kanban-dots {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 2px;
    padding: 2px 0 8px;
  }
  .kanban-dot {
    position: relative;
    min-width: 30px;
    min-height: 28px;
    border: none;
    background: transparent;
    padding: 0;
    cursor: pointer;
  }
  .kanban-dot::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 7px;
    height: 7px;
    border-radius: var(--radius-pill);
    background: var(--border-strong);
    transition: width 0.22s ease, background 0.22s ease;
  }
  .kanban-dot.active::after {
    width: 22px;
    background: var(--dot-c, var(--accent));
  }
  .kanban-dot:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-pill);
  }
  /* Nav-Kanban v2 (ADR-238): the data-active-stage display:none column
     filter was RETIRED here — the carousel shows every column; the
     attribute stays on the board (JSX) as the chips' active-state
     source and a harmless hook. */
}

@media (max-width: 768px) and (min-width: 601px) {
  /* 2 cols: محجوز + في الانتظار on row 1, مع الطبيب + انتهت on row 2,
     لم يحضر + ملغي on row 3. The grid-auto-rows let each pair size
     naturally to their tallest card stack. This tier is phone-landscape +
     small-tablet-portrait only (phones portrait get the chip view up to
     600px — Nav-Kanban 2026-07-05).
     2026-07-18: with cancelled on the board the stage count went 5 -> 6, so
     the rows divide evenly and the odd-one-out full-row span that used to
     carry لم يحضر is no longer needed (it would now leave a hole). */
  .kanban-board.kanban-full-width {
    grid-template-columns: 1fr 1fr !important;
    overflow-x: visible !important;
    padding-bottom: 0 !important;
    gap: var(--space-2-5);
  }
  /* Cards inside the 2-col layout get tighter padding so the
     primary CTA stays readable at ~165px-wide cells (iPhone 14).
     The !important is gone (2026-07-18): it existed only to out-rank the JSX
     inline padding, which no longer exists. Plain source order now decides. */
  .kanban-board.kanban-full-width .kanban-card {
    padding: var(--space-3);
  }
  .kanban-board.kanban-full-width .kanban-card-primary-row {
    flex-wrap: wrap;
  }
}

@media (max-width: 1023px) and (min-width: 769px) {
  /* 3 cols using a 6-fr base:
       Row 1: محجوز(span 2) | في الانتظار(span 2) | مع الطبيب(span 2)
       Row 2: انتهت(span 2) | لم يحضر(span 2) | ملغي(span 2)
     Mapping by data-stage keeps the ordering intentional (active
     stages on row 1, settled stages on row 2) so the receptionist
     sees the working set first.
     2026-07-18: row 2 was a 3+3 split of two stages; cancelled joining the
     board makes it three, so every stage spans 2 and both rows are even. */
  .kanban-board.kanban-full-width {
    grid-template-columns: repeat(6, 1fr) !important;
    overflow-x: visible !important;
    padding-bottom: 0 !important;
    gap: var(--space-3);
  }
  /* Descendant (not >) — see the ADR-238 strip-wrapper note above. */
  .kanban-board.kanban-full-width .kanban-col[data-stage="pending"],
  .kanban-board.kanban-full-width .kanban-col[data-stage="confirmed"],
  .kanban-board.kanban-full-width .kanban-col[data-stage="in-progress"],
  .kanban-board.kanban-full-width .kanban-col[data-stage="done"],
  .kanban-board.kanban-full-width .kanban-col[data-stage="no_show"],
  .kanban-board.kanban-full-width .kanban-col[data-stage="cancelled"] {
    grid-column: span 2;
  }
}

/* Kanban card breathing room where the columns COMPETE (2026-07-18, owner:
   "شكلها ضيق"). Measured at 1280px after cancelled joined the board: a column
   is 148px, but the nesting chrome — column border (2) + cards-stack padding
   (20) + card padding (28) + the accent rail (4) — ate 54px, leaving the card
   just 98px of real content for a name, a doctor select and the CTA.

   Reclaiming from the chrome rather than from the column count: the gap gives
   back 20px across the row, and the two paddings give ~14px to every card.
   Same six columns, visibly wider cards.

   Scoped to >=769px ON PURPOSE. Below that the board is 2-up (329px cards) or
   the phone carousel (80% viewport), where this same padding reads generous
   rather than cramped — tightening it there would make the cards look cheap.
   Placed after the tier blocks above so source order wins (identical 0,2,0
   specificity), which is also why no !important is needed. */

@media (min-width: 769px) {
  .kanban-board.kanban-full-width {
    gap: var(--space-2);
  }
  .kanban-board.kanban-full-width .kanban-cards-stack {
    padding: var(--space-1-5);
  }
  .kanban-board.kanban-full-width .kanban-card {
    padding: 11px;
  }
}

/* ADR-143: route-level motion restraint.
   The old global page/card entrance animations made refresh and screen
   navigation feel like a second temporary screen was loading. Keep
   micro-interactions and modal animations, but make the main route body
   settle immediately once the shell is visible. */

.main-content > .fade-in,
.main-content .page-header {
  animation: none !important;
}

/* Card-stagger neutralizer RETIRED (W-REVEAL 2026-07-17): this !important
   override was a band-aid that killed the parallel card slideUp on
   .main-content so it would not fight the reveal. The base rules it fought
   are now gone (retired above), so the override has nothing left to do. The
   .fade-in / .page-header neutralizer above stays - those base rules live. */

/* ============================================
   Announcements Wave 1.5 (2026-05-31)
   ============================================
   Originally shipped a custom slide-in @keyframes for the
   announcements-drawer pattern. After owner feedback we switched
   to the ResponsiveModal primitive (centered modal on desktop,
   bottom-sheet on mobile) — the keyframes was retired because
   ResponsiveModal owns the animation lifecycle. Only the card
   hover rule below remains. */

.announcements-card:hover {
  background: var(--bg-hover) !important;
}

/* ============================================
   Announcements Wave 2 (2026-05-31) — premium polish
   ============================================ */

/* --- Topbar icon: severity-toned pulse halo --- */

.ann-icon-btn {
  position: relative;
}

.ann-icon-pulse {
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  animation: annPulseGlow 2.4s ease-out infinite;
  z-index: 0;
}

.ann-icon-btn > svg,
.ann-icon-btn > .ann-icon-badge {
  position: relative;
  z-index: 1;
}

.ann-icon-pulse--info {
  background: radial-gradient(circle, color-mix(in srgb, var(--info, #3b82f6) 50%, transparent) 0%, transparent 65%);
}

.ann-icon-pulse--success {
  background: radial-gradient(circle, color-mix(in srgb, var(--success, #22c55e) 55%, transparent) 0%, transparent 65%);
}

.ann-icon-pulse--warning {
  background: radial-gradient(circle, color-mix(in srgb, var(--warning, #f59e0b) 55%, transparent) 0%, transparent 65%);
}

.ann-icon-pulse--danger {
  background: radial-gradient(circle, color-mix(in srgb, var(--danger, #ef4444) 60%, transparent) 0%, transparent 65%);
}

@keyframes annPulseGlow {
  0%   { opacity: 0;   transform: scale(0.85); }
  50%  { opacity: 0.85; transform: scale(1.25); }
  100% { opacity: 0;   transform: scale(1.45); }
}

/* Badge — fixed positioning + size for visual consistency with the
   ann-icon-pulse halo layering above. */

.ann-icon-badge {
  position: absolute;
  top: -3px;
  inset-inline-end: -3px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  /* R2 W1d (owner reference): count badge reads ACCENT (informational count,
     matching the reference shot), not danger red - urgency still has its own
     channel via the ann-icon-pulse tones. Elevated ring so it pops off the
     ghost glyph cleanly. */
  background: var(--accent, #0ea5b7);
  color: #fff;
  border-radius: var(--radius-pill);
  font-size: var(--text-micro);
  font-weight: 900;
  line-height: 1;
  display: grid;
  place-items: center;
  box-shadow: 0 0 0 2px var(--bg-elevated, var(--bg, #fff));
}

/* --- Card severity tone stripe (4px inline-start bar) --- */

.announcements-card {
  position: relative;
}

.announcements-card::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--info, #3b82f6);
}

.announcements-card--success::before { background: var(--success, #22c55e); }

.announcements-card--info::before    { background: var(--info,    #3b82f6); }

.announcements-card--warning::before { background: var(--warning, #f59e0b); }

.announcements-card--danger::before  { background: var(--danger,  #ef4444); }

/* --- Inline media preview (image + video) --- */

.ann-media {
  width: 100%;
  border-radius: var(--radius);
  overflow: hidden;
  margin: 12px 0 4px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
}

.ann-media img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 320px;
  object-fit: cover;
}

.ann-media-video {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
}

.ann-media-video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* --- Confetti shimmer on first-mount of a `feature` card --- */

.ann-confetti {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(circle at 18% 28%, color-mix(in srgb, var(--success, #22c55e) 50%, transparent) 0%, transparent 26%),
    radial-gradient(circle at 78% 68%, color-mix(in srgb, var(--success, #22c55e) 35%, transparent) 0%, transparent 28%),
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--success, #22c55e) 14%, transparent) 0%, transparent 60%);
  animation: annConfettiShimmer 700ms ease-out;
  opacity: 0;
  z-index: 0;
}

.announcements-card > * {
  position: relative;
  z-index: 1;
}

@keyframes annConfettiShimmer {
  0%   { opacity: 0; transform: scale(0.96); }
  45%  { opacity: 1; transform: scale(1.02); }
  100% { opacity: 0; transform: scale(1.06); }
}

/* --- Login splash — sparkle float --- */

@keyframes annSparkleFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* --- Loading skeleton (panel) --- */

.announcements-panel__skeleton {
  display: grid;
  gap: 1px;
}

.ann-skeleton-card {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}

.ann-skeleton-line {
  height: 12px;
  background: linear-gradient(
    90deg,
    var(--border) 0%,
    var(--bg-subtle) 50%,
    var(--border) 100%
  );
  background-size: 200% 100%;
  animation: annSkeletonShimmer 1.6s ease-in-out infinite;
  border-radius: var(--radius-sm);
  margin-bottom: 8px;
}

.ann-skeleton-line:last-child { margin-bottom: 0; }

@keyframes annSkeletonShimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* --- Editor split-screen (form + live preview) --- */

.ann-editor-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--space-5);
  align-items: start;
}

.ann-editor-preview {
  position: sticky;
  top: 12px;
  padding: 14px;
  background: var(--bg-subtle);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  max-height: 70vh;
  overflow-y: auto;
}

.ann-editor-preview__label {
  font-size: var(--text-meta);
  font-weight: 800;
  color: var(--text-tertiary);
  margin-bottom: 10px;
  letter-spacing: 0.02em;
}

.ann-editor-preview__frame {
  background: var(--bg, #fff);
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.ann-editor-preview__frame .announcements-card {
  border-bottom: 0;
}

/* Collapse to single column on tablets / phones — preview goes below the form. */

@media (max-width: 920px) {
  .ann-editor-split {
    grid-template-columns: 1fr;
  }
  .ann-editor-preview {
    position: static;
    max-height: none;
  }
}

/* ============================================
   Announcements Wave 2.1 (2026-05-31)
   Login splash redesign — hero preview with severity badge,
   title, body, and inline image/video.
   ============================================ */

.ann-login-modal__body {
  text-align: center;
  padding: 6px 4px 14px;
}

.ann-login-modal__sparkle {
  font-size: var(--text-display);
  margin-bottom: 10px;
  animation: annSparkleFloat 2.2s ease-in-out infinite;
  line-height: 1;
}

.ann-login-modal__chip-row {
  display: flex;
  justify-content: center;
  margin-bottom: 10px;
}

.ann-login-modal__title {
  font-weight: 900;
  font-size: 20px;
  color: var(--text-primary);
  line-height: 1.3;
  margin-bottom: 10px;
}

.ann-login-modal__body-text {
  font-size: 13.5px;
  color: var(--text-secondary);
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 12px;
  text-align: start;
  padding-inline: 6px;
}

.ann-login-modal__media {
  margin: 8px auto 0;
  max-width: 100%;
}

.ann-login-modal__media iframe,
.ann-login-modal__media img {
  display: block;
}

/* ============================================
   Announcements Wave 2.2 (2026-05-31)
   Live preview language toggle (AR / EN) inside the editor.
   ============================================ */

.ann-editor-preview__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
  margin-bottom: 10px;
}

.ann-editor-preview__lang-toggle {
  display: flex;
  align-items: center;
}

/* ============================================
   Announcements Wave 3 (2026-05-31)
   Analytics modal: KPI grid, per-plan bars, recent viewers.
   ============================================ */

.ann-anl-body {
  display: grid;
  gap: 18px;
}

.ann-anl-kpis {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-2-5);
}

@media (max-width: 920px) {
  .ann-anl-kpis { grid-template-columns: repeat(2, 1fr); }
}

.ann-anl-kpi {
  padding: var(--space-3);
  border-radius: 10px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  display: grid;
  gap: var(--space-1);
  text-align: center;
}

.ann-anl-kpi__icon {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  margin: 0 auto 4px;
  background: var(--bg-elevated, #fff);
  color: var(--text-secondary);
}

.ann-anl-kpi__label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-weight: 600;
}

.ann-anl-kpi--accent  .ann-anl-kpi__icon { background: color-mix(in srgb, var(--accent, #0ea5b7) 15%, transparent); color: var(--accent, #0ea5b7); }

.ann-anl-kpi--success .ann-anl-kpi__icon { background: color-mix(in srgb, var(--success, #22c55e) 15%, transparent); color: var(--success, #22c55e); }

.ann-anl-kpi--info    .ann-anl-kpi__icon { background: color-mix(in srgb, var(--info, #3b82f6) 15%, transparent); color: var(--info, #3b82f6); }

.ann-anl-kpi--warning .ann-anl-kpi__icon { background: color-mix(in srgb, var(--warning, #f59e0b) 15%, transparent); color: var(--warning, #f59e0b); }

.ann-anl-kpi--danger  .ann-anl-kpi__icon { background: color-mix(in srgb, var(--danger, #ef4444) 15%, transparent); color: var(--danger, #ef4444); }

.ann-anl-section {
  padding: 14px;
  background: var(--bg-subtle);
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

.ann-anl-section__title {
  font-weight: 800;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-bottom: 10px;
}

.ann-anl-bars { display: grid; gap: var(--space-3); }

.ann-anl-bar { display: grid; gap: var(--space-1-5); }

.ann-anl-bar__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2-5);
}

.ann-anl-bar__track {
  background: var(--border);
  height: 8px;
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.ann-anl-bar__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent, #0ea5b7), color-mix(in srgb, var(--accent, #0ea5b7) 60%, transparent));
  border-radius: var(--radius-pill);
  transition: width .35s ease;
}

.ann-anl-recent {
  display: grid;
  gap: 1px;
  background: var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.ann-anl-recent__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2-5);
  padding: 10px 12px;
  background: var(--bg, #fff);
  font-size: var(--text-sm);
}

/* ============================================================
   AI Assistant Wave 1 (2026-05-31)
   See AI_ASSISTANT_PLAN.md sec 3 (Layer 1 - Frontend Shell).
   ============================================================ */

/* ---- Floating ball ---- */

/* AI Wave 1 hotfix (2026-05-31): owner asked to move the ball to the
   opposite side (RTL bottom-LEFT) so it stops overlapping the avatar
   /clinic-name block at the bottom-right. inset-inline-end resolves
   to LEFT in RTL and RIGHT in LTR — natural for both directions.
   Desktop bumped from 56px to 64px for stronger presence; mobile
   stays at 48px below the media query. */

.ai-ball {
  position: fixed;
  bottom: 22px;
  inset-inline-end: 22px;
  z-index: 180;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  background: linear-gradient(135deg, var(--accent, #6c63ff), var(--accent-2, #ff6c63));
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 30px rgba(108, 99, 255, 0.42), 0 4px 12px rgba(0, 0, 0, 0.12);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.ai-ball:hover {
  transform: translateY(-4px) scale(1.04);
  box-shadow: 0 16px 38px rgba(108, 99, 255, 0.55), 0 6px 18px rgba(0, 0, 0, 0.18);
}

.ai-ball:active { transform: translateY(0) scale(0.98); }

.ai-ball:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

.ai-ball__icon { display: flex; align-items: center; justify-content: center; }

.ai-ball__pulse {
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(108, 99, 255, 0.42) 0%, transparent 70%);
  animation: ai-ball-pulse 2.4s ease-out infinite;
  pointer-events: none;
}

@keyframes ai-ball-pulse {
  0%   { transform: scale(1);   opacity: 0.55; }
  70%  { transform: scale(1.35); opacity: 0; }
  100% { transform: scale(1.35); opacity: 0; }
}

/* ---- First-visit nudge ---- */

.ai-ball-nudge {
  position: fixed;
  bottom: 98px;
  inset-inline-end: 22px;
  z-index: 181;
  max-width: 260px;
  padding: 12px 14px 12px 14px;
  background: var(--bg, #fff);
  border: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  border-radius: 14px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.18);
  cursor: pointer;
  animation: ai-nudge-in 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.ai-ball-nudge::after {
  content: '';
  position: absolute;
  bottom: -8px;
  inset-inline-end: 24px;
  width: 14px;
  height: 14px;
  background: var(--bg, #fff);
  border-right: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  transform: rotate(45deg);
}

.ai-ball-nudge__close {
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text-tertiary, #999);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ai-ball-nudge__close:hover { background: var(--bg-subtle, rgba(0, 0, 0, 0.04)); }

.ai-ball-nudge__title { font-size: var(--text-sm); font-weight: 800; color: var(--text-primary, #111); padding-inline-end: 22px; }

.ai-ball-nudge__body  { font-size: var(--text-meta); color: var(--text-secondary, #666); margin-top: 4px; line-height: 1.5; }

@keyframes ai-nudge-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- Panel ---- */

/* Owner 2026-07-18: Modir now sits in the central ChatModal, which owns the
   card height and makes `.rm-body` an unpadded flex column. So the panel FILLS
   that column (instead of sizing itself with its own min/max-height) and pays
   back the padding the shell no longer applies — the shell owns structure, the
   skin owns look. `.ai-panel__thread` stays the only scroller. */

.ai-panel__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  gap: 0;
  padding: var(--pad-modal-premium-sm) var(--pad-modal-premium);
}

.ai-panel__thread {
  flex: 1;
  overflow-y: auto;
  padding: 8px 4px 12px;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ---- «الوارد» (Chrome round 3, owner 2026-08-13) ----------------------------
   The clinic room and the platform-updates feed lost their topbar buttons; the
   bar was giving 69% of a 412px row to chrome and 17% to the screen's name.
   These are LAUNCHER rows, not rehosted panels: each opens the surface that
   already exists, so there is still exactly one implementation of each inbox.
   Built from house tokens only — no bespoke palette, so all 12 themes work. */

.ai-inbox {
  flex: 1;
  overflow-y: auto;
  padding: 10px 4px 12px;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.ai-inbox__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg-card);
  cursor: pointer;
  text-align: start;
  font-family: inherit;
  transition: border-color var(--duration-fast) var(--ease-smooth),
              background var(--duration-fast) var(--ease-smooth);
}

.ai-inbox__row:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--state-hover-surface, var(--bg-subtle));
}

.ai-inbox__row:focus-visible { outline: none; box-shadow: var(--ring-shadow); }

.ai-inbox__row:disabled { opacity: 0.5; cursor: not-allowed; }

/* Same tinted-glass identity chip the screen headers use, smallest size. */

.ai-inbox__icon {
  width: 38px;
  height: 38px;
  border-radius: 11px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  color: var(--accent);
  background:
    linear-gradient(145deg,
      color-mix(in srgb, var(--accent) 22%, var(--bg-elevated)),
      color-mix(in srgb, var(--accent) 6%, var(--bg-elevated)));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
}

.ai-inbox__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }

.ai-inbox__title { font-size: var(--text-sm); font-weight: 700; color: var(--text-primary); }

.ai-inbox__desc {
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ai-inbox__count {
  flex-shrink: 0;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 800;
  display: grid;
  place-items: center;
  line-height: 1;
}

.ai-inbox__chev { color: var(--text-tertiary); flex-shrink: 0; }

[dir="ltr"] .ai-inbox__chev { transform: scaleX(-1); }

@media (prefers-reduced-motion: reduce) {
  .ai-inbox__row { transition: none; }
}

/* ---- Message bubble ---- */

.ai-msg {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2-5);
}

.ai-msg--user { flex-direction: row-reverse; }

.ai-msg__avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  background: var(--bg-subtle, rgba(0, 0, 0, 0.04));
  color: var(--text-secondary, #666);
}

.ai-msg--assistant .ai-msg__avatar {
  background: linear-gradient(135deg, var(--accent, #6c63ff), var(--accent-2, #ff6c63));
  color: #fff;
}

.ai-msg--user .ai-msg__avatar {
  background: var(--accent, #6c63ff);
  color: #fff;
}

.ai-msg__body {
  max-width: 80%;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.ai-msg--user .ai-msg__body { align-items: flex-end; }

.ai-msg__role {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-tertiary, #888);
}

.ai-msg__content {
  background: var(--bg-subtle, rgba(0, 0, 0, 0.04));
  border-radius: 14px;
  padding: 9px 12px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-primary, #111);
  white-space: pre-wrap;
  word-break: break-word;
}

.ai-msg--user .ai-msg__content {
  background: var(--accent, #6c63ff);
  color: #fff;
}

.ai-msg--system {
  justify-content: center;
}

.ai-msg__system-text {
  font-size: var(--text-meta);
  color: var(--text-tertiary, #888);
  background: var(--bg-subtle, rgba(0, 0, 0, 0.04));
  padding: 6px 12px;
  border-radius: var(--radius);
  text-align: center;
}

.ai-msg--pending .ai-msg__content { background: transparent; padding: 0; }

.ai-msg__typing {
  display: flex;
  gap: var(--space-1);
  padding: 10px 12px;
  background: var(--bg-subtle, rgba(0, 0, 0, 0.04));
  border-radius: 14px;
  width: fit-content;
}

.ai-msg__typing span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-tertiary, #999);
  animation: ai-typing 1.4s ease-in-out infinite;
}

.ai-msg__typing span:nth-child(2) { animation-delay: 0.2s; }

.ai-msg__typing span:nth-child(3) { animation-delay: 0.4s; }

@keyframes ai-typing {
  0%, 60%, 100% { transform: scale(0.85); opacity: 0.45; }
  30% { transform: scale(1.05); opacity: 1; }
}

/* ---- Composer ---- */

.ai-panel__composer {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 10px 4px 6px;
  border-top: 1px solid var(--border, rgba(0, 0, 0, 0.08));
}

.ai-panel__input {
  flex: 1;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  border-radius: var(--radius);
  padding: 10px 14px;
  font-size: 13.5px;
  background: var(--bg, #fff);
  color: var(--text-primary, #111);
  outline: none;
}

.ai-panel__input:focus { border-color: var(--accent, #6c63ff); }

.ai-panel__input:disabled { opacity: 0.6; cursor: not-allowed; }

.ai-panel__send {
  border: 0;
  background: var(--accent, #6c63ff);
  color: #fff;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

/* Modir's send is its own 38px circle, so this row owns its height the same way
   a SectionCard head does: the mic matches its NEIGHBOUR, not a global constant.
   That is the rule in all three places the mic appears - beside Save it is 32,
   beside «إضافة» 34, beside Send 38 - and it is why the mic ships one default
   plus a rule per row that has a size of its own, rather than one size that is
   subtly wrong everywhere. */

.ai-panel__composer .voice-mic-btn { height: 38px; min-width: 38px; }

.ai-panel__training-voice {
  display: flex;
  gap: 4px;
  flex: 0 0 auto;
}

.ai-panel__training-voice-example {
  min-height: 32px;
  padding: 0 8px;
  border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--border));
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  color: var(--text-2);
  font: inherit;
  font-size: 10px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}

.ai-panel__training-voice-example:hover {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  color: var(--accent);
}

.ai-panel__training-questions {
  display: flex;
  gap: 6px;
  padding: 4px 12px 8px;
  overflow-x: auto;
  scrollbar-width: none;
}

.ai-panel__training-questions::-webkit-scrollbar { display: none; }

.ai-panel__training-question {
  flex: 0 0 auto;
  max-width: 250px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text-2);
  font: inherit;
  font-size: 11px;
  text-align: start;
  cursor: pointer;
}

.ai-panel__training-question:hover {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  color: var(--text);
}

.ai-panel__training-playbooks {
  display: grid;
  gap: 6px;
  padding: 6px 12px 10px;
  border-top: 1px solid var(--border);
}

.ai-panel__training-playbooks-title {
  color: var(--text-2);
  font-size: 11px;
  font-weight: 800;
}

.ai-panel__training-playbook {
  display: grid;
  gap: 6px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 4%, var(--surface));
}

.ai-panel__training-playbook-copy {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 11px;
}

.ai-panel__training-playbook-copy span { color: var(--text-3); font-size: 10px; }

.ai-panel__training-playbook-choices { display: flex; gap: 5px; overflow-x: auto; }

.ai-panel__training-playbook-choices .ai-panel__training-question { padding: 5px 8px; font-size: 10px; }

.ai-panel__training-voice-link {
  max-width: 150px;
  border: 0;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 10px;
  font-weight: 700;
  cursor: pointer;
}

.ai-panel__send:hover:not(:disabled) { transform: scale(1.05); }

.ai-panel__send:disabled { opacity: 0.4; cursor: not-allowed; }

/* ---- Disclaimer ---- */

.ai-panel__disclaimer {
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  font-size: 10.5px;
  line-height: 1.55;
  color: var(--text-secondary, #888);
  text-align: center;
}

/* ---- Footer ---- */

.ai-panel__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding-top: 6px;
}

.ai-panel__footer-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  padding: 6px 10px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.1));
  border-radius: 10px;
  background: transparent;
  color: var(--text-secondary, #666);
  font-size: var(--text-xs);
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
}

.ai-panel__footer-btn:hover:not(:disabled) { background: var(--bg-subtle, rgba(0, 0, 0, 0.04)); }

.ai-panel__footer-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.ai-panel__footer-btn.is-on {
  background: var(--brand-soft, rgba(8, 145, 178, 0.10));
  border-color: var(--brand, #0891b2);
  color: var(--brand, #0891b2);
}

/* ---- W2-5: daily budget meter + conversation history ---- */

.ai-panel__coin-consumption {
  display: flex;
  align-items: center;
  gap: 7px;
  width: fit-content;
  max-width: 100%;
  padding: 6px 10px;
  border: 1px solid color-mix(in srgb, var(--brand, #0891b2) 28%, transparent);
  border-radius: var(--radius-pill, 999px);
  background: color-mix(in srgb, var(--brand, #0891b2) 8%, transparent);
  color: var(--text-secondary, #526071);
  font-size: 11px;
  font-weight: 700;
}

.ai-panel__coin-consumption svg { color: var(--brand, #0891b2); }

.ai-panel__coin-consumption strong {
  color: var(--text-primary, #111827);
  font-size: 12px;
  white-space: nowrap;
}

.ai-panel__quota {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 4px 2px 0;
}

.ai-panel__quota-bar {
  flex: 1;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle, rgba(0, 0, 0, 0.06));
  overflow: hidden;
}

.ai-panel__quota-fill {
  display: block;
  height: 100%;
  background: var(--brand, #0891b2);
  transition: width .3s ease;
}

.ai-panel__quota-text {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--text-tertiary, #888);
  white-space: nowrap;
}

.ai-panel__quota.is-warn     .ai-panel__quota-fill { background: #f59e0b; }

.ai-panel__quota.is-critical .ai-panel__quota-fill { background: #ef4444; }

.ai-panel__quota.is-critical .ai-panel__quota-text { color: #ef4444; }

.ai-panel__history {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px 0;
}

.ai-panel__history-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.1));
  border-radius: 12px;
  background: transparent;
  color: var(--text-primary, #111);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  text-align: start;
}

.ai-panel__history-row:hover { background: var(--bg-subtle, rgba(0, 0, 0, 0.04)); }

.ai-panel__history-row.is-current {
  border-color: var(--brand, #0891b2);
  background: var(--brand-soft, rgba(8, 145, 178, 0.08));
}

.ai-panel__history-when  { font-weight: 700; }

.ai-panel__history-count {
  margin-inline-start: auto;
  font-size: var(--text-xs);
  color: var(--text-tertiary, #888);
}

.ai-panel__history-empty {
  padding: 24px 12px;
  text-align: center;
  color: var(--text-tertiary, #888);
  font-size: var(--text-sm);
}

.ai-panel__footer-btn--support {
  background: #25d366;
  color: #fff;
  border-color: #25d366;
}

.ai-panel__footer-btn--support:hover { background: #1ebe5c; }

/* .ai-kb-tab__* removed 2026-07-28 (settings unification): the tab renders
   through SettingsSection + SettingField + SettingsRow now. Its private card,
   form and row markup were a sixth card dialect inside one screen. */

/* ============================================================
   AI Assistant Wave 2 - Inline action / tool cards inside chat.
   ============================================================ */

.ai-msg__cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: 8px;
  width: 100%;
  max-width: 520px;
}

.ai-msg__error {
  margin-top: 6px;
  padding: 6px 10px;
  background: rgba(220, 53, 69, 0.1);
  color: #b8001e;
  border-radius: var(--radius-md);
  font-size: var(--text-meta);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

/* An error the user can act on gets its action inline, so "upgrade your plan"
   is a button and not just advice. (ADR-268) */

.ai-msg__error-cta {
  align-self: flex-start;
}

/* Plan-gated Modir: the central EmptyState carries the upsell, this only
   centres it in the panel body the chat would have filled. */

.ai-panel__gate {
  display: grid;
  place-items: center;
  padding: 28px 20px 36px;
}

.ai-card {
  background: var(--bg, #fff);
  border: 1px solid var(--border, rgba(0, 0, 0, 0.08));
  border-radius: var(--radius);
  padding: 10px 12px;
  font-size: var(--text-sm);
  color: var(--text-primary, #111);
}

.ai-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: 8px;
}

/* W2-3: data cards open collapsed - the head IS the toggle, so it has to
   read as a control (full width, pointer, no button chrome). */

.ai-card__head--toggle {
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: start;
  margin-bottom: 0;
}

.ai-card--data.is-open .ai-card__head--toggle { margin-bottom: 8px; }

.ai-card__head--toggle .ai-card__title { flex: 1; }

.ai-card__title { font-size: var(--text-sm); font-weight: 800; color: var(--text-primary, #111); }

.ai-card__tool-name {
  font-size: 10.5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--bg-subtle, rgba(0, 0, 0, 0.04));
  color: var(--text-tertiary, #888);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
}

.ai-card__empty {
  text-align: center;
  color: var(--text-tertiary, #888);
  padding: var(--space-2-5);
  font-size: var(--text-meta);
}

/* Navigate card */

.ai-card--navigate {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: linear-gradient(135deg, rgba(14, 165, 183, 0.10), rgba(34, 197, 94, 0.06));
  border-color: rgba(14, 165, 183, 0.35);
  cursor: pointer;
  font-weight: 700;
  width: fit-content;
  max-width: 100%;
  text-align: start;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.ai-card--navigate:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(14, 165, 183, 0.15);
}

.ai-card__navigate-label { color: var(--text-primary, #111); }

.ai-card__navigate-screen {
  font-size: 10.5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: rgba(14, 165, 183, 0.12);
  color: #0a6c79;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  margin-inline-start: auto;
}

/* Video card */

.ai-card--video { padding: var(--space-2); }

.ai-card__video { width: 100%; }

.ai-card__video-title {
  font-size: 12.5px;
  font-weight: 700;
  margin-bottom: 6px;
  color: var(--text-primary, #111);
}

.ai-card__video-frame-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #000;
}

.ai-card__video-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.ai-card__video-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1-5);
  padding: 20px 12px;
  background: var(--bg-subtle, rgba(0, 0, 0, 0.04));
  border-radius: 10px;
  color: var(--text-tertiary, #888);
  text-align: center;
}

.ai-card__video-placeholder-title { font-size: var(--text-sm); font-weight: 800; color: var(--text-secondary, #555); }

.ai-card__video-placeholder-sub   { font-size: 11.5px; }

/* KPI tiles */

.ai-card__kpis {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-1-5);
}

.ai-card__kpi-tile {
  background: var(--bg-subtle, rgba(0, 0, 0, 0.03));
  border-radius: var(--radius-md);
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ai-card__kpi-label  { font-size: 10.5px; color: var(--text-tertiary, #888); font-weight: 700; }

.ai-card__kpi-value  { font-size: var(--text-base); font-weight: 800; color: var(--text-primary, #111); }

.ai-card__kpi-suffix { font-size: var(--text-micro); color: var(--text-tertiary, #999); }

/* Grouped bars */

.ai-card__bars { display: flex; flex-direction: column; gap: var(--space-1-5); }

.ai-card__bar-row {
  display: grid;
  grid-template-columns: minmax(70px, 30%) 1fr auto;
  align-items: center;
  gap: var(--space-1-5);
}

.ai-card__bar-label { font-size: 11.5px; color: var(--text-secondary, #555); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ai-card__bar-track {
  position: relative;
  height: 8px;
  background: var(--bg-subtle, rgba(0, 0, 0, 0.06));
  border-radius: var(--radius-xs);
  overflow: hidden;
}

.ai-card__bar-fill {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  background: linear-gradient(90deg, #0ea5b7, #22c55e);
  border-radius: var(--radius-xs);
  transition: width 0.3s ease;
}

.ai-card__bar-value { font-size: 11.5px; font-weight: 700; color: var(--text-primary, #111); min-width: 40px; text-align: end; }

/* Anomalies list */

.ai-card__anomalies { display: flex; flex-direction: column; gap: var(--space-1-5); }

.ai-card__anomaly {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 6px 10px;
  border-radius: var(--radius-md);
  background: var(--bg-subtle, rgba(0, 0, 0, 0.03));
  border-inline-start: 3px solid var(--text-tertiary, #999);
}

.ai-card__anomaly--high   { border-inline-start-color: #dc3545; background: rgba(220, 53, 69, 0.05); }

.ai-card__anomaly--medium { border-inline-start-color: #f59e0b; background: rgba(245, 158, 11, 0.05); }

.ai-card__anomaly-kind  { font-size: var(--text-meta); font-weight: 700; color: var(--text-primary, #111); }

.ai-card__anomaly-meta  { display: flex; align-items: center; gap: var(--space-2); }

.ai-card__anomaly-label { font-size: var(--text-xs); color: var(--text-tertiary, #888); }

.ai-card__anomaly-value { font-size: var(--text-meta); font-weight: 800; color: var(--text-secondary, #555); font-family: ui-monospace, monospace; }

/* Entity rows */

.ai-card__rows { display: flex; flex-direction: column; gap: var(--space-1); }

.ai-card__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px 8px;
  border-radius: var(--radius-md);
  background: var(--bg-subtle, rgba(0, 0, 0, 0.03));
}

.ai-card__row-title { flex: 1; font-size: 12.5px; font-weight: 700; color: var(--text-primary, #111); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ai-card__row-sub   { font-size: 11.5px; font-weight: 800; color: var(--text-secondary, #555); }

.ai-card__row-chip  { font-size: 10.5px; padding: 2px 6px; border-radius: var(--radius-sm); background: rgba(0, 0, 0, 0.05); color: var(--text-tertiary, #888); }

/* Tool call working indicator */

.ai-card--working {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px 12px;
  background: rgba(14, 165, 183, 0.08);
  border-color: rgba(14, 165, 183, 0.2);
  color: var(--text-secondary, #555);
  font-size: 11.5px;
  font-weight: 700;
  width: fit-content;
}

.ai-card__working-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #0ea5b7;
  animation: ai-card-pulse 1.2s ease-in-out infinite;
}

@keyframes ai-card-pulse {
  0%, 100% { transform: scale(0.8); opacity: 0.4; }
  50%      { transform: scale(1.2); opacity: 1; }
}

/* Tool call error */

.ai-card--error {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: rgba(220, 53, 69, 0.07);
  border-color: rgba(220, 53, 69, 0.25);
  color: #b8001e;
  font-size: 11.5px;
  width: fit-content;
}

/* Mobile (<=768px) tweaks - keep ball smaller per owner ask. */

@media (max-width: 768px) {
  .ai-ball { width: 48px; height: 48px; bottom: 84px; inset-inline-end: 14px; }
  .ai-ball-nudge { bottom: 142px; inset-inline-end: 14px; max-width: 220px; }
  /* (min/max-height dropped 2026-07-18: ChatModal owns the card height on
     every breakpoint, and an 80vh cap here stopped the panel filling the
     ~94dvh mobile sheet.) */
  .ai-msg__body { max-width: 88%; }
  .ai-card__kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ai-msg__cards { max-width: 100%; }
}

/* =================================================================
   AI Assistant Wave 3 - Behavior Watchers + Insights + Wins
   ----------------------------------------------------------------
   See AI_ASSISTANT_PLAN.md sec 10 Wave 3 (ADR-201 + ADR-202).
   Three view modes inside the panel (chat/insights/wins) + ball
   badge + observation banner + insight card + value prompt.
   ================================================================= */

/* Ball unread badge (Wave 3). */

.ai-ball__badge {
  position: absolute;
  top: 4px;
  inset-inline-end: 4px;
  background: var(--danger, #dc3545);
  color: #fff;
  font-size: var(--text-micro);
  font-weight: 700;
  min-width: 18px;
  height: 18px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
  box-shadow: 0 0 0 2px var(--bg-base, #fff);
  line-height: 1;
}

.ai-ball__badge--dot {
  min-width: 12px;
  width: 12px;
  height: 12px;
  padding: 0;
}

/* Panel-tabs family removed (Phase 3 batch B, 2026-07-10): the Modir
   panel strip rides the ONE Tabs primitive + central .tab-count pill —
   its hardcoded-hex badge + brand-teal actives die with it. */

/* Observation banner (C). */

.ai-observation-banner {
  display: flex;
  gap: var(--space-2-5);
  align-items: flex-start;
  margin: 10px 12px;
  padding: 10px 12px;
  background: var(--surface-info, rgba(8, 145, 178, 0.07));
  border: 1px solid var(--brand-soft-border, rgba(8, 145, 178, 0.18));
  border-radius: var(--radius);
}

.ai-observation-banner__icon {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  background: var(--brand-soft, rgba(8, 145, 178, 0.12));
  color: var(--brand, #0891b2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.ai-observation-banner__body { flex: 1; min-width: 0; }

.ai-observation-banner__title {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text, #1d2331);
  margin-block-end: 2px;
}

.ai-observation-banner__sub {
  font-size: var(--text-meta);
  color: var(--text-muted, #5d6b80);
  line-height: 1.5;
  margin-block-end: 6px;
}

.ai-observation-banner__bar {
  width: 100%;
  height: 4px;
  background: rgba(8, 145, 178, 0.12);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.ai-observation-banner__bar-fill {
  height: 100%;
  background: var(--brand, #0891b2);
  border-radius: var(--radius-pill);
  transition: width 240ms ease-out;
}

/* Insights view. */

.ai-insights-view {
  padding: 8px 12px 12px;
  overflow-y: auto;
  /* W1b: flex-fill instead of a fixed height cap - a long list used to
     overflow the modal body and scroll the TAB STRIP off-screen (the
     owner-reported cannot-return-to-chat bug). min-height:0 is what lets
     a flex child actually shrink; the view is the ONLY scroller now. */
  flex: 1;
  min-height: 0;
}

/* W3-A (R12): the board says when it was computed. Deliberately quiet -
   it is a disclosure, not a headline. */

.ai-insights-view__fresh {
  font-size: 11.5px;
  color: var(--text-muted, #5d6b80);
  padding: 0 var(--space-1);
  margin-block-end: var(--space-1);
}

.ai-insights-view__training-note {
  margin: 8px 12px 2px;
  padding: 9px 10px;
  border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--border));
  border-radius: 10px;
  background: color-mix(in srgb, var(--brand) 7%, var(--surface));
  color: var(--text-2);
  font-size: 11px;
  line-height: 1.55;
}

.ai-insights-view__filters {
  display: flex;
  flex-direction: column;
  gap: var(--space-1-5);
  margin-block-end: 10px;
}

.ai-insights-view__filter-row {
  display: flex;
  gap: var(--space-1-5);
  flex-wrap: wrap;
}

.ai-insights-chip {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius-pill);
  padding: 4px 11px;
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text-muted, #5d6b80);
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.ai-insights-chip:hover {
  /* Hover Wave 3a: --surface-hover was never defined anywhere, so the
     rgba fallback always rendered - near-invisible on dark themes. The
     central state token (== --bg-hover) themes correctly. Same fix on
     the three sibling AI hover rules below. */
  background: var(--state-hover-surface);
  color: var(--text, #1d2331);
}

.ai-insights-chip.is-on {
  background: var(--brand, #0891b2);
  color: #fff;
  border-color: var(--brand, #0891b2);
}

.ai-insights-chip--status.is-on {
  background: var(--text, #1d2331);
  border-color: var(--text, #1d2331);
}

.ai-insights-view__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.ai-insights-view__loading,
.ai-insights-view__empty {
  text-align: center;
  padding: 32px 12px;
  color: var(--text-muted, #5d6b80);
  font-size: var(--text-sm);
}

.ai-insights-view__empty-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto 8px;
  border-radius: 50%;
  background: var(--success-soft, rgba(16, 185, 129, 0.12));
  color: var(--success, #10b981);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.ai-insights-view__empty-title {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--text, #1d2331);
  margin-block-end: 4px;
}

/* Insight card. */

.ai-insight-card {
  position: relative;
  background: var(--surface, #fff);
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: 14px;
  padding: 12px 14px 10px;
  overflow: hidden;
  transition: transform 120ms, box-shadow 120ms;
}

.ai-insight-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 6px 16px rgba(15, 25, 50, 0.06);
}

.ai-insight-card.is-dismissed,
.ai-insight-card.is-acted {
  opacity: 0.62;
}

.ai-insight-card__stripe {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 3px;
  background: var(--brand, #0891b2);
}

.ai-insight-card--critical .ai-insight-card__stripe { background: var(--danger, #dc3545); }

.ai-insight-card--high .ai-insight-card__stripe     { background: var(--warning, #f59e0b); }

.ai-insight-card--medium .ai-insight-card__stripe   { background: var(--brand, #0891b2); }

.ai-insight-card--low .ai-insight-card__stripe      { background: var(--text-muted, #5d6b80); }

.ai-insight-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-block-end: 6px;
  font-size: 11.5px;
}

.ai-insight-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: var(--radius-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--text-micro);
}

.ai-insight-chip--critical { background: rgba(220, 53, 69, 0.12); color: #b8001e; }

.ai-insight-chip--high     { background: rgba(245, 158, 11, 0.14); color: #b35e00; }

.ai-insight-chip--medium   { background: rgba(8, 145, 178, 0.12); color: var(--brand, #0891b2); }

.ai-insight-chip--low      { background: rgba(93, 107, 128, 0.12); color: var(--text-muted, #5d6b80); }

.ai-insight-card__category {
  color: var(--text-muted, #5d6b80);
}

.ai-insight-card__subject {
  margin-inline-start: auto;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  background: rgba(0,0,0,0.04);
  padding: 2px 7px;
  border-radius: var(--radius-sm);
  color: var(--text, #1d2331);
  font-size: var(--text-xs);
}

/* Migration 298 roll-up: one card standing for N findings. The chip
   reads as a COUNT (not a severity), so it borrows the neutral subject
   styling rather than a tone colour. */

.ai-insight-card__rollup {
  margin-inline-start: auto;
  background: rgba(8, 145, 178, 0.10);
  color: var(--brand, #0891b2);
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

.ai-insight-card__subject + .ai-insight-card__rollup { margin-inline-start: 6px; }

/* W3-A (Migration 301): the living card shows its own movement. Rising
   is red because every rolled-up watcher counts a PROBLEM - more of it
   is worse - and falling is green for the same reason. */

.ai-insight-card__delta {
  margin-inline-start: auto;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

.ai-insight-card__rollup + .ai-insight-card__delta { margin-inline-start: 6px; }

.ai-insight-card__delta--up {
  background: rgba(220, 38, 38, 0.10);
  color: var(--danger, #dc2626);
}

.ai-insight-card__delta--down {
  background: rgba(22, 163, 74, 0.10);
  color: var(--success, #16a34a);
}

.ai-insight-card__body {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--text, #1d2331);
  margin-block-end: 8px;
}

.ai-insight-card__examples {
  margin: -2px 0 8px;
  padding-inline-start: 16px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted, #5d6b80);
}

.ai-insight-card__examples li { margin: 1px 0; }

.ai-insight-card__action-row {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  flex-wrap: wrap;
  margin-block-end: 6px;
}

.ai-insight-card__action-btn,
.ai-insight-card__act-btn,
.ai-insight-card__dismiss-btn {
  appearance: none;
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  background: var(--surface-soft, #f7f8fa);
  border-radius: var(--radius-md);
  padding: 5px 10px;
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text, #1d2331);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  transition: background 120ms, transform 120ms;
}

.ai-insight-card__action-btn {
  background: var(--brand, #0891b2);
  color: #fff;
  border-color: var(--brand, #0891b2);
}

.ai-insight-card__action-btn:hover { transform: translateY(-4px); }

.ai-insight-card__act-btn:hover    { background: var(--state-hover-surface); }

.ai-insight-card__dismiss-btn      { margin-inline-start: auto; padding: 5px; }

.ai-insight-card__feedback {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  font-size: 11.5px;
  color: var(--text-muted, #5d6b80);
  border-block-start: 1px dashed var(--border-soft, rgba(0,0,0,0.06));
  padding-block-start: 6px;
}

.ai-insight-card__feedback-label { margin-inline-end: 4px; }

.ai-insight-card__thumb {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius-md);
  padding: 3px 8px;
  cursor: pointer;
  color: var(--text-muted, #5d6b80);
  transition: background 120ms, border-color 120ms, color 120ms;
}

.ai-insight-card__thumb:hover {
  background: var(--state-hover-surface);
}

.ai-insight-card__thumb.is-on {
  background: var(--brand-soft, rgba(8, 145, 178, 0.12));
  border-color: var(--brand, #0891b2);
  color: var(--brand, #0891b2);
}

.ai-insight-card__value-prompt {
  margin-block-start: 8px;
  padding: 8px 10px;
  background: var(--surface-soft, #f7f8fa);
  border: 1px dashed var(--border-soft, rgba(0,0,0,0.1));
  border-radius: 10px;
}

.ai-insight-card__value-label {
  font-size: var(--text-meta);
  color: var(--text-muted, #5d6b80);
  margin-block-end: 6px;
}

.ai-insight-card__value-row {
  display: flex;
  gap: var(--space-1-5);
  flex-wrap: wrap;
  align-items: center;
}

.ai-insight-card__value-kind {
  appearance: none;
  background: var(--surface, #fff);
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius-md);
  padding: 5px 8px;
  font-size: 12.5px;
  color: var(--text, #1d2331);
  cursor: pointer;
}

.ai-insight-card__value-amount {
  flex: 1;
  min-width: 120px;
  background: var(--surface, #fff);
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius-md);
  padding: 5px 8px;
  font-size: 12.5px;
}

.ai-insight-card__value-save,
.ai-insight-card__value-skip {
  appearance: none;
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  background: var(--surface, #fff);
  border-radius: var(--radius-md);
  padding: 5px 12px;
  font-size: var(--text-meta);
  font-weight: 600;
  cursor: pointer;
}

.ai-insight-card__value-save {
  background: var(--brand, #0891b2);
  color: #fff;
  border-color: var(--brand, #0891b2);
}

/* Wins view (B - Value Dashboard). */

.ai-wins-view {
  padding: 8px 12px 12px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}

.ai-wins-view__period-row {
  display: flex;
  gap: var(--space-1-5);
  flex-wrap: wrap;
  margin-block-end: 10px;
}

.ai-wins-chip {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text-muted, #5d6b80);
  cursor: pointer;
}

.ai-wins-chip.is-on {
  background: var(--brand, #0891b2);
  color: #fff;
  border-color: var(--brand, #0891b2);
}

.ai-wins-view__hero {
  background: linear-gradient(135deg, rgba(8, 145, 178, 0.08), rgba(34, 197, 94, 0.06));
  border: 1px solid var(--brand-soft-border, rgba(8, 145, 178, 0.16));
  border-radius: 14px;
  padding: 16px 14px;
  text-align: center;
  margin-block-end: 12px;
}

.ai-wins-view__hero-label {
  font-size: var(--text-meta);
  color: var(--text-muted, #5d6b80);
  margin-block-end: 4px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.ai-wins-view__hero-value {
  font-size: 28px;
  font-weight: 800;
  color: var(--success, #10b981);
  line-height: 1.1;
  margin-block-end: 6px;
}

.ai-wins-view__hero-meta {
  font-size: var(--text-meta);
  color: var(--text-muted, #5d6b80);
}

.ai-wins-view__breakdown {
  background: var(--surface, #fff);
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius);
  padding: 10px 12px;
}

.ai-wins-view__breakdown-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text, #1d2331);
  margin-block-end: 8px;
}

.ai-wins-view__breakdown-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1-5);
}

.ai-wins-view__breakdown-row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  background: var(--surface-soft, #f7f8fa);
  border-radius: var(--radius-md);
  font-size: 12.5px;
  overflow: hidden;
}

.ai-wins-view__breakdown-bar {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  background: rgba(8, 145, 178, 0.12);
  z-index: 0;
}

.ai-wins-view__breakdown-kind,
.ai-wins-view__breakdown-amount {
  position: relative;
  z-index: 1;
}

.ai-wins-view__breakdown-amount {
  font-weight: 700;
  color: var(--text, #1d2331);
}

.ai-wins-view__footer-note {
  margin-block-start: 10px;
  font-size: 11.5px;
  color: var(--text-muted, #5d6b80);
  text-align: center;
  line-height: 1.55;
}

/* SaaS Admin AI Watchers tab. */

.saas-ai-watchers { padding: 16px 0; }

.saas-ai-watchers__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-block-end: 12px;
}

.saas-ai-watchers__title { font-size: 16px; font-weight: 700; margin: 0; }

.saas-ai-watchers__reload {
  appearance: none;
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  background: var(--surface-soft, #f7f8fa);
  border-radius: var(--radius-md);
  padding: 6px 12px;
  font-size: 12.5px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  cursor: pointer;
}

.saas-ai-watchers__totals {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2-5);
  margin-block-end: 14px;
}

.saas-ai-watchers__total {
  background: var(--surface, #fff);
  border: 1px solid var(--border-soft, rgba(0,0,0,0.08));
  border-radius: var(--radius);
  padding: 12px 14px;
}

.saas-ai-watchers__total-label {
  font-size: 11.5px;
  color: var(--text-muted, #5d6b80);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-block-end: 4px;
}

.saas-ai-watchers__total-value {
  font-size: 20px;
  font-weight: 800;
  color: var(--text, #1d2331);
}

.saas-ai-watchers__table-wrap { overflow-x: auto; }

.saas-ai-watchers__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}

.saas-ai-watchers__table th,
.saas-ai-watchers__table td {
  padding: 8px 10px;
  border-block-end: 1px solid var(--border-soft, rgba(0,0,0,0.06));
  text-align: start;
}

.saas-ai-watchers__table th {
  background: var(--surface-soft, #f7f8fa);
  font-weight: 700;
  color: var(--text-muted, #5d6b80);
}

.saas-ai-watchers__table tr.is-suppressed { opacity: 0.5; }

.saas-ai-watchers__name {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-weight: 700;
  font-size: var(--text-meta);
}

.saas-ai-watchers__desc {
  font-size: 11.5px;
  color: var(--text-muted, #5d6b80);
  margin-block-start: 2px;
  max-width: 320px;
}

.saas-ai-watchers__forbidden {
  text-align: center;
  padding: 32px 16px;
  color: var(--text-muted, #5d6b80);
}

.saas-ai-watchers__error {
  background: rgba(220, 53, 69, 0.07);
  border: 1px solid rgba(220, 53, 69, 0.2);
  color: #b8001e;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  font-size: 12.5px;
  margin-block-end: 10px;
}

/* W1b: the panel's tab strip is pinned - it never shrinks nor scrolls
   with the views (the views are the scrollers). */

.ai-panel__body > .tabs { flex-shrink: 0; }

/* W1c: admin AI model-picker rows (the live vendor catalog dropdown). */

.ai-model-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; background: transparent; border: 0; cursor: pointer;
  padding: 6px 8px; border-radius: var(--radius-sm);
  font-family: ui-monospace, monospace; font-size: 12.5px; color: var(--text-primary);
}

.ai-model-row:hover, .ai-model-row.is-active { background: var(--state-hover-surface); }

/* Mobile tweaks for Wave 3 views. */

@media (max-width: 768px) {
  .saas-ai-watchers__totals { grid-template-columns: 1fr; }
  .ai-insight-card__action-row { gap: var(--space-1); }
}

/* =================================================================
   AI Watchers Settings Tab (Wave 3.5)
   ================================================================= */

/* .ai-watchers-tab__* / .ai-watchers-row* / .ai-watchers-threshold* removed
   2026-07-28 (settings unification). The tab now uses SettingsSection per
   category, SettingsRow per watcher and the central Toggle - the hand-rolled
   pill in here was the FIFTH switch shape in the app and the only one that
   wrote to the database on click. */

/* ── W3-B: the ONE «مُدير» settings tab ────────────────────────────── */

.modir-tab { display: flex; flex-direction: column; gap: var(--space-2); }

/* .modir-tab__* removed 2026-07-28: the three collapsibles are plain sections
   now (SectionHead + SectionCard), so the tab opens showing its contents
   instead of three closed drawers. .modir-usage* below is still live. */

.modir-usage__bar {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-2, #eef2f7);
  overflow: hidden;
}

.modir-usage__fill { height: 100%; background: var(--brand, #0891b2); }

.modir-usage--warn   .modir-usage__fill { background: var(--warning, #d97706); }

.modir-usage--danger .modir-usage__fill { background: var(--danger, #dc2626); }

.modir-usage__nums {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  margin-block-start: 6px;
}

.modir-usage__pct { font-weight: 800; font-size: 15px; }

.modir-usage__of  { font-size: 12px; color: var(--text-muted, #5d6b80); }

/* ============================================================
   Support / Contact center (Wave 1) - hub + admin editor.
   Fully responsive via auto-fit grid + flex-wrap rows; no
   breakpoint-specific markup needed.
   ============================================================ */

.sup-section { display: grid; gap: var(--space-3); }

.sup-section__head { display: grid; gap: 2px; }

.sup-section__title { font-size: 18px; font-weight: 800; margin: 0; color: var(--text-primary); }

.sup-section__hint { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; }

.sup-loading { padding: var(--space-6); text-align: center; color: var(--text-tertiary); }

.sup-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-3);
}

.sup-card {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 14px 16px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  text-decoration: none; color: var(--text-primary);
  transition: border-color 0.15s, background 0.15s, transform 0.15s;
}

.sup-card:hover {
  border-color: var(--accent);
  background: var(--bg-hover);
  transform: translateY(var(--lift-1));
}

.sup-card__icon {
  width: 44px; height: 44px; border-radius: var(--radius);
  display: grid; place-items: center; flex-shrink: 0;
  background: var(--accent-soft); color: var(--accent-text);
}

.sup-card__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }

.sup-card__name { font-weight: 700; font-size: var(--text-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sup-card__value { font-size: var(--text-meta); color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sup-card__go { color: var(--text-tertiary); flex-shrink: 0; }

/* Subtle per-channel icon tints for instant recognition (still on-brand). */

.sup-card--whatsapp .sup-card__icon { background: var(--success-soft); color: var(--success); }

.sup-card--youtube  .sup-card__icon { background: var(--danger-soft);  color: var(--danger); }

.sup-card--facebook .sup-card__icon,
.sup-card--linkedin .sup-card__icon,
.sup-card--x        .sup-card__icon { background: var(--info-soft); color: var(--info); }

/* Admin editor */

.sup-admin { display: grid; gap: 14px; }

.sup-admin__head-icon {
  width: 40px; height: 40px; border-radius: 10px;
  display: grid; place-items: center; flex-shrink: 0;
  background: var(--accent-soft); color: var(--accent-text);
}

.sup-admin__ch { padding: 14px 16px; }

.sup-admin__ch-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 10px; }

.sup-admin__ch-icon {
  width: 30px; height: 30px; border-radius: var(--radius-md);
  display: grid; place-items: center; flex-shrink: 0;
  background: var(--accent-soft); color: var(--accent-text);
}

.sup-admin__ch-name { font-size: var(--text-base); font-weight: 800; color: var(--text-primary); }

.sup-admin__row { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 8px; flex-wrap: wrap; }

.sup-admin__row-value { flex: 2 1 220px; min-width: 0; }

.sup-admin__row-label { flex: 1 1 160px; min-width: 0; }

.sup-admin__row-toggle { flex-shrink: 0; }

.sup-admin__row-del { flex-shrink: 0; color: var(--danger); }

.sup-admin__empty { font-size: var(--text-sm); color: var(--text-tertiary); padding: 4px 0 10px; }

.sup-admin__add { margin-top: 2px; }

.sup-admin__savebar {
  display: flex; align-items: center; gap: var(--space-3);
  position: sticky; bottom: 0;
  padding: 12px 0 4px;
  background: linear-gradient(to top, var(--bg) 60%, transparent);
}

.sup-admin__dirty { font-size: var(--text-meta); color: var(--warning); }

/* Support center Wave 2 - feedback form + tickets + admin inbox */

.sup-feedback-row {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 860px) { .sup-feedback-row { grid-template-columns: 1fr; } }

.sup-form, .sup-tickets-card { padding: var(--space-4); display: grid; gap: var(--space-3); }

.sup-form__cats { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.sup-form__field { display: grid; gap: 6px; }

.sup-form__label { color: var(--text-secondary); font-size: var(--text-sm); font-weight: 700; }

.sup-chip {
  font-size: var(--text-meta); padding: 6px 12px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--bg-subtle);
  color: var(--text-secondary); cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.sup-chip:hover { border-color: var(--border-strong); }

.sup-chip.is-on { background: var(--accent-soft); color: var(--accent-text); border-color: var(--accent); font-weight: 700; }

.sup-form__msg { resize: vertical; min-height: 90px; font-family: inherit; line-height: 1.6; }

.sup-form__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2-5); flex-wrap: wrap; }

.sup-form__note { font-size: var(--text-xs); color: var(--text-tertiary); }

.sup-tickets { display: grid; gap: var(--space-2-5); max-height: 440px; overflow: auto; }

.sup-tickets-empty { font-size: var(--text-sm); color: var(--text-tertiary); padding: 18px; text-align: center; }

.sup-ticket {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 11px 12px; background: var(--bg-card); display: grid; gap: var(--space-1-5);
}

.sup-ticket__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }

.sup-ticket__subject { font-weight: 700; font-size: 13.5px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

.sup-ticket__meta { font-size: var(--text-xs); color: var(--text-tertiary); }

.sup-ticket__msg { font-size: 12.5px; color: var(--text-secondary); line-height: 1.6; white-space: pre-wrap; word-break: break-word; }

.sup-ticket__reply {
  font-size: 12.5px; color: var(--text-secondary); line-height: 1.6;
  background: var(--bg-subtle); border-radius: var(--radius-sm); padding: 8px 10px;
}

.sup-ticket__reply-label { font-weight: 700; color: var(--success); }

.sup-status { font-size: var(--text-xs); padding: 2px 9px; border-radius: var(--radius-pill); white-space: nowrap; flex-shrink: 0; }

.sup-status--open { background: var(--warning-soft); color: var(--warning); }

.sup-status--answered { background: var(--success-soft); color: var(--success); }

.sup-status--closed { background: var(--bg-subtle); color: var(--text-tertiary); }

.sup-inbox { padding: var(--space-4); display: grid; gap: var(--space-3); }

.sup-inbox__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }

.sup-inbox__filters { display: flex; flex-wrap: wrap; gap: var(--space-1-5); }

.sup-inbox__list { display: grid; gap: var(--space-3); }

.sup-inbox__row-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.sup-inbox__row-actions .select { min-width: 130px; }

/* Support Wave 2: expandable "my tickets" card (reveals the clinic's own message + reply) */

.sup-ticket { padding: 0; gap: 0; overflow: hidden; }

.sup-ticket__bar {
  display: flex; align-items: center; gap: var(--space-2-5); width: 100%;
  padding: 11px 12px; background: transparent; border: 0;
  cursor: pointer; text-align: start; color: inherit; font: inherit;
  transition: background 0.15s;
}

.sup-ticket__bar:hover { background: var(--bg-hover); }

.sup-ticket__bar-main { display: grid; gap: 2px; flex: 1; min-width: 0; }

.sup-ticket__chev { color: var(--text-tertiary); flex-shrink: 0; transition: transform 0.18s; }

.sup-ticket.is-open .sup-ticket__chev { transform: rotate(180deg); }

.sup-ticket__detail {
  padding: 10px 12px 12px; display: grid; gap: var(--space-2-5);
  border-top: 1px solid var(--border);
}

.sup-ticket__block { display: grid; gap: 3px; }

.sup-ticket__block-label { font-size: var(--text-xs); font-weight: 700; color: var(--text-tertiary); }

.sup-ticket__pending { font-size: var(--text-meta); color: var(--text-tertiary); font-style: italic; }

.sup-wa-btn {
  justify-self: start; align-self: start;
  background: var(--success-soft); color: var(--success);
  border: 1px solid var(--success); text-decoration: none;
}

.sup-wa-btn:hover { background: var(--success); color: #fff; }

/* ===== Shared purchase-price visuals ================================ */

.bourse-spark { display: block; }

.bourse-spark polyline { stroke-linecap: round; stroke-linejoin: round; }

.bourse-spark--up polyline { stroke: var(--danger); }

.bourse-spark--down polyline { stroke: var(--success); }

.bourse-spark--flat polyline { stroke: var(--text-muted); }

.bourse-detail { display: flex; flex-direction: column; gap: var(--space-4); }

.bourse-banner {
  display: flex; align-items: flex-start; gap: var(--space-2-5);
  padding: 12px 14px; border-radius: var(--radius); font-size: var(--text-sm); line-height: 1.6;
}

.bourse-banner-icon { flex: none; margin-top: 1px; }

.bourse-banner--danger { background: var(--danger-soft); color: var(--danger); }

.bourse-banner--success { background: var(--success-soft); color: var(--success); }

.bourse-banner--muted { background: var(--accent-soft); color: var(--text-secondary); }

.bourse-chart-wrap { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 14px; }

.bourse-chart-title { font-size: var(--text-meta); font-weight: 600; color: var(--text-secondary); margin-bottom: 10px; }

.bourse-chart-empty { padding: 28px 12px; text-align: center; color: var(--text-secondary); font-size: var(--text-sm); }

/* ── Central premium trend chart (Analytics + Material Bourse) ──────────
   One visual/interaction grammar: live legend, ruler tooltip, smart labels,
   touch scrub, keyboard arrows and restrained entrance motion. */

.trend-chart { min-width: 0; }

.trend-chart__empty {
  padding: 28px; text-align: center; color: var(--text-tertiary); font-size: var(--text-sm);
}

.trend-chart__legend {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
  margin-bottom: 8px;
}

.trend-chart__legend-item {
  appearance: none; border: 1px solid transparent; background: transparent;
  color: var(--text-secondary); border-radius: var(--radius-pill);
  display: inline-flex; align-items: center; gap: 7px; min-height: 30px;
  padding: 5px 9px; font: inherit; font-size: var(--text-xs); cursor: pointer;
  transition: background .18s ease, border-color .18s ease, color .18s ease,
    transform .18s ease;
}

@media (hover: hover) {
  .trend-chart__legend-item:hover {
    background: var(--bg-subtle); border-color: var(--border); color: var(--text-primary);
    transform: translateY(var(--lift-1));
  }
}

.trend-chart__legend-item.is-active {
  background: var(--bg-subtle); border-color: color-mix(in srgb, var(--series-color, var(--accent)) 28%, var(--border));
  color: var(--text-primary);
}

.trend-chart__legend-swatch {
  width: 16px; height: 4px; flex: 0 0 auto; border-radius: var(--radius-pill);
  background: var(--series-color); box-shadow: 0 0 0 3px color-mix(in srgb, var(--series-color) 10%, transparent);
}

.trend-chart__legend-label { font-weight: 700; }

.trend-chart__legend-value {
  color: var(--text-primary); font-weight: 900; font-variant-numeric: tabular-nums;
}

.trend-chart__gap-chip {
  display: inline-flex; align-items: center; gap: var(--space-1-5); min-height: 28px;
  padding: 4px 9px; border-radius: var(--radius-pill); font-size: 10.5px; font-weight: 800;
}

.trend-chart__gap-chip.is-good { color: var(--success); background: color-mix(in srgb, var(--success) 10%, transparent); }

.trend-chart__gap-chip.is-bad { color: var(--danger); background: color-mix(in srgb, var(--danger) 10%, transparent); }

.trend-chart__gap-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

.trend-chart__stage { position: relative; width: 100%; min-width: 0; }

.trend-chart__progress {
  position: absolute; z-index: 4; inset: 0 0 auto; height: 2px; overflow: hidden;
  border-radius: var(--radius-pill); background: color-mix(in srgb, var(--accent) 9%, transparent);
}

.trend-chart__progress::after {
  content: ''; position: absolute; inset-block: 0; width: 34%; border-radius: inherit;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: trend-chart-progress .72s var(--ease-smooth) infinite;
}

.trend-chart__scroll { position: relative; width: 100%; min-width: 0; }

.trend-chart__scroll.is-scrollable {
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-inline: contain;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}

.trend-chart__scroll.is-scrollable::-webkit-scrollbar { display: none; }

.trend-chart__scroll.is-scrollable .trend-chart__svg { touch-action: pan-x pan-y; }

.trend-chart__canvas { position: relative; min-width: 0; }

.trend-chart__svg {
  display: block; overflow: visible; direction: ltr; touch-action: pan-y;
  outline: none; cursor: crosshair;
}

.trend-chart__series {
  transform-box: fill-box; transform-origin: center;
  animation: trend-chart-series-in .42s cubic-bezier(.22,.8,.28,1) both;
}

.trend-chart__svg:focus-visible {
  filter: drop-shadow(0 0 0 color-mix(in srgb, var(--accent) 26%, transparent));
}

.trend-chart__grid {
  stroke: color-mix(in srgb, var(--border) 78%, transparent); stroke-width: .8;
  stroke-dasharray: 3 6;
}

.trend-chart__axis-value,
.trend-chart__axis-label {
  fill: var(--text-tertiary); font-family: inherit; font-size: 9px; font-weight: 650;
}

.trend-chart__axis-label { font-size: var(--text-micro); }

.trend-chart__area,
.trend-chart__band { animation: trend-chart-fade .65s ease-out both; }

.trend-chart__glow {
  fill: none; stroke-width: 10; stroke-linecap: round; stroke-linejoin: round;
  filter: blur(4px); transition: opacity .18s ease;
}

.trend-chart__line {
  fill: none; stroke-width: 2.65; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  animation: trend-chart-draw .85s cubic-bezier(.22,.8,.28,1) forwards;
  transition: opacity .18s ease, stroke-width .18s ease;
}

.trend-chart__point {
  stroke: var(--card-bg, var(--card)); stroke-width: 1.5;
  transition: opacity .18s ease;
}

.trend-chart__data-label {
  font-family: inherit; font-size: var(--text-micro); font-weight: 900;
  paint-order: stroke; stroke: var(--card-bg, var(--card)); stroke-width: 4px;
  stroke-linejoin: round; pointer-events: none;
}

.trend-chart__ruler {
  stroke: var(--text-tertiary); stroke-width: 1; stroke-dasharray: 3 5; opacity: .55;
  transition: x1 .12s ease-out, x2 .12s ease-out;
}

.trend-chart__halo { opacity: .13; animation: trend-chart-pulse 1.8s ease-out infinite; }

.trend-chart__active-point {
  stroke: var(--card-bg, var(--card)); stroke-width: 2.5;
  filter: drop-shadow(0 2px 3px rgba(15,23,42,.18));
}

.trend-chart__hit-area { fill: transparent; pointer-events: all; }

.trend-chart__tooltip {
  --chart-tip-x: 50%; --chart-tip-y: 80px;
  position: absolute; z-index: 3; left: var(--chart-tip-x); top: var(--chart-tip-y);
  transform: translate(-50%, -100%); width: max-content; min-width: 170px; max-width: 210px;
  padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
  border-radius: 13px; background: color-mix(in srgb, var(--card-bg, var(--card)) 94%, transparent);
  box-shadow: 0 12px 30px rgba(15,23,42,.13), 0 2px 8px rgba(15,23,42,.07);
  backdrop-filter: blur(12px); pointer-events: none; direction: rtl;
  animation: trend-chart-tooltip-in .28s ease-out both;
  transition: left .12s ease-out, top .12s ease-out;
}

.trend-chart__tooltip-date {
  color: var(--text-tertiary); font-size: var(--text-micro); font-weight: 800;
  padding-bottom: 6px; margin-bottom: 5px; border-bottom: 1px solid var(--border);
}

.trend-chart__tooltip-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  min-height: 21px; color: var(--text-secondary); font-size: var(--text-xs);
}

.trend-chart__tooltip-row strong { font-size: var(--text-meta); font-weight: 900; font-variant-numeric: tabular-nums; }

.trend-chart__tooltip-key { display: inline-flex; align-items: center; gap: var(--space-1-5); }

.trend-chart__tooltip-key i { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }

.trend-chart__tooltip-row--gap {
  margin-top: 3px; padding-top: 5px; border-top: 1px dashed var(--border); font-size: 10.5px;
}

.trend-chart__tooltip-row .is-good { color: var(--success); }

.trend-chart__tooltip-row .is-bad { color: var(--danger); }

.trend-chart__totals {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2);
  margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--border);
}

.trend-chart__total { text-align: center; min-width: 0; }

.trend-chart__total strong { display: block; font-size: var(--text-sm); font-weight: 900; font-variant-numeric: tabular-nums; }

.trend-chart__total span { display: block; margin-top: 2px; color: var(--text-tertiary); font-size: 9px; }

.analytics-trend-controls {
  display: flex; flex-direction: column; gap: var(--space-1-5); align-items: flex-end;
}

/* ChartFilter — the explicit per-chart scope selector (owner 2026-07-21).
   Sits in the same controls column as the frame picker, so a chart's "what
   period" and "whose numbers" read as one cluster. Compact by design: it must
   never out-shout the chart it labels. */

.chart-filter { display: flex; align-items: center; gap: var(--space-1-5); flex-wrap: wrap; justify-content: flex-end; }

.chart-filter__select {
  appearance: none;
  font: inherit; font-size: var(--text-xs); font-weight: 700;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 4px 24px 4px 10px;
  max-width: 150px;
  cursor: pointer;
  /* Chevron drawn as a background so the pill keeps its shape in both
     directions (a native arrow flips side and breaks the RTL pill). */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 12px) calc(50% + 1px), calc(100% - 8px) calc(50% + 1px);
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
  transition: border-color var(--duration-fast) var(--ease-smooth), color var(--duration-fast) var(--ease-smooth);
}

.chart-filter__select:hover { border-color: var(--border-strong); }

.chart-filter__select.is-active {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  background-color: var(--accent-soft);
}

.chart-filter__clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: none;
  border-radius: var(--radius-pill); cursor: pointer;
  color: var(--text-tertiary);
  background: transparent; border: 1px solid var(--border);
}

.chart-filter__clear:hover { color: var(--danger); border-color: var(--danger); }

@media (max-width: 540px) {
  .chart-filter {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%; align-items: stretch;
  }
  .chart-filter__select { width: 100%; min-width: 0; max-width: none; }
  .chart-filter__clear { grid-column: 1 / -1; justify-self: end; }
}

@keyframes trend-chart-draw { to { stroke-dashoffset: 0; } }

@keyframes trend-chart-fade { from { opacity: 0; } to { opacity: 1; } }

@keyframes trend-chart-series-in {
  from { opacity: .28; transform: translateY(4px) scaleY(.985); }
  to { opacity: 1; transform: translateY(0) scaleY(1); }
}

@keyframes trend-chart-progress {
  from { inset-inline-start: -38%; }
  to { inset-inline-start: 104%; }
}

@keyframes trend-chart-tooltip-in {
  from { opacity: 0; transform: translate(-50%, calc(-100% + 5px)) scale(.98); }
  to { opacity: 1; transform: translate(-50%, -100%) scale(1); }
}

@keyframes trend-chart-pulse {
  0%, 45% { transform: scale(.7); transform-origin: center; opacity: .18; }
  100% { transform: scale(1.45); transform-origin: center; opacity: 0; }
}

@media (max-width: 540px) {
  .analytics-trend-card > .section-head { display: grid; grid-template-columns: minmax(0, 1fr); align-items: stretch; }
  .analytics-trend-card > .section-head .section-actions { width: 100%; }
  .analytics-trend-controls { width: 100%; align-items: stretch; }
  .analytics-trend-controls .seg-control { display: flex; width: 100%; }
  .analytics-trend-controls .seg-control__item { min-width: 0; padding-inline: 4px; font-size: var(--text-xs); }
  .trend-chart__legend { gap: 4px 6px; margin-bottom: 4px; }
  .trend-chart__legend-item { min-height: 28px; padding: 4px 7px; }
  .trend-chart__legend-value { font-size: 10.5px; }
  .trend-chart__tooltip {
    width: 176px; min-width: 176px; max-width: 176px; padding: 7px 9px;
    border-radius: 11px; transition: left .08s linear, top .08s linear;
  }
  .trend-chart__tooltip-date { padding-bottom: 4px; margin-bottom: 3px; }
  .trend-chart__tooltip-row { min-height: 18px; gap: var(--space-2); font-size: var(--text-micro); }
  .trend-chart__tooltip-row strong { font-size: 10.5px; }
  .trend-chart__axis-label { font-size: 9px; }
  .trend-chart__data-label { font-size: 9px; }
  .trend-chart__totals { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* useIsPhone is the component's central breakpoint authority. These mirrors
   keep the layout correct in embedded/narrow shells whose browser viewport is
   wider than the chart itself (desktop app preview, split view, modal). */

.trend-chart.is-phone .trend-chart__legend { gap: 4px 6px; margin-bottom: 4px; }

.trend-chart.is-phone .trend-chart__legend-item { min-height: 28px; padding: 4px 7px; }

.trend-chart.is-phone .trend-chart__legend-value { font-size: 10.5px; }

.trend-chart.is-phone .trend-chart__tooltip {
  width: 176px; min-width: 176px; max-width: 176px; padding: 7px 9px;
  border-radius: 11px; transition: left .08s linear, top .08s linear;
}

.trend-chart.is-phone .trend-chart__tooltip-date { padding-bottom: 4px; margin-bottom: 3px; }

.trend-chart.is-phone .trend-chart__tooltip-row { min-height: 18px; gap: var(--space-2); font-size: var(--text-micro); }

.trend-chart.is-phone .trend-chart__tooltip-row strong { font-size: 10.5px; }

.trend-chart.is-phone .trend-chart__axis-label,
.trend-chart.is-phone .trend-chart__data-label { font-size: 9px; }

.trend-chart.is-phone .trend-chart__totals { grid-template-columns: repeat(2, minmax(0, 1fr)); }

@media (prefers-reduced-motion: reduce) {
  .trend-chart__line, .trend-chart__area, .trend-chart__band,
  .trend-chart__tooltip, .trend-chart__halo, .trend-chart__series,
  .trend-chart__progress::after { animation: none !important; }
  .trend-chart__line { stroke-dashoffset: 0; }
  .trend-chart__tooltip, .trend-chart__ruler { transition: none; }
}

/* W10/W11 (2026-07-19): the bourse price chart is drawn by the central
   TrendChart engine and its supplier key by the central ChartLegend — the
   old percent-coordinate plot classes (-plot/-svg/-area-fill/-line/-grid/
   -ylab/-dot/-plabel/-xaxis/-xlab/-leg/-leg-sw) were RETIRED with it. */

.bourse-chartx { display: flex; flex-direction: column; gap: var(--space-1-5); }

.bourse-chartx-legend { display: flex; flex-wrap: wrap; gap: 5px 14px; }

.bourse-detail-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--space-2-5); }

/* .bourse-mini removed (KpiCard P5): price mini-stats live on dense MetricTile. */

.bourse-suppliers { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 12px 14px; }

.bourse-suppliers-title { font-size: var(--text-sm); font-weight: 800; color: var(--text-primary); margin-bottom: 8px; }

.bourse-sup-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2-5); padding: 8px 0; border-top: 1px solid var(--border); }

.bourse-sup-row:first-of-type { border-top: none; }

.bourse-sup-left { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }

.bourse-sup-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }

.bourse-sup-name { font-size: var(--text-sm); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bourse-sup-right { display: inline-flex; align-items: center; gap: var(--space-2); flex: none; }

.bourse-sup-avg { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }

.bourse-sup-best { font-size: var(--text-xs); font-weight: 600; color: var(--success); background: var(--success-soft); padding: 2px 8px; border-radius: var(--radius-pill); }

@media (max-width: 600px) {
  .bourse-detail-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ===== Central actual-vs-benchmark meter ==============================
   Used wherever a row needs to communicate a relationship, not two naked
   numbers. The fill is the observed value; the hairline is the benchmark. */

.comparison-meter {
  --comparison-color: var(--accent);
  min-width: 150px;
  display: grid;
  gap: 7px;
}

.comparison-meter.is-success { --comparison-color: var(--success); }

.comparison-meter.is-warning { --comparison-color: var(--warning); }

.comparison-meter.is-danger { --comparison-color: var(--danger); }

.comparison-meter.is-info { --comparison-color: var(--info, var(--accent)); }

.comparison-meter.is-muted,
.comparison-meter.is-neutral { --comparison-color: var(--text-tertiary); }

.comparison-meter__head,
.comparison-meter__legend {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.comparison-meter__head strong {
  color: var(--text-primary);
  font-size: 11.5px;
  font-weight: 800;
}

.comparison-meter__delta {
  max-width: 130px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--comparison-color);
  font-size: var(--text-micro);
  font-weight: 800;
}

.comparison-meter__track {
  position: relative;
  direction: ltr;
  height: 7px;
  overflow: visible;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--border) 70%, transparent);
}

.comparison-meter__fill {
  position: absolute;
  inset: 0 auto 0 var(--comparison-start);
  width: var(--comparison-fill);
  border-radius: inherit;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--comparison-color) 58%, transparent),
    var(--comparison-color));
  transition: width .42s var(--ease-smooth);
}

.comparison-meter__benchmark {
  position: absolute;
  z-index: 1;
  left: var(--comparison-benchmark);
  top: -3px;
  width: 2px;
  height: 13px;
  border-radius: var(--radius-pill);
  background: var(--text-primary);
  box-shadow: 0 0 0 2px var(--card-bg, var(--card));
  transform: translateX(-1px);
}

.comparison-meter__legend {
  justify-content: flex-start;
  gap: var(--space-3);
  color: var(--text-tertiary);
  font-size: 9.5px;
}

.comparison-meter__legend > span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
}

.comparison-meter__legend b {
  color: var(--text-secondary);
  font-size: var(--text-micro);
  font-weight: 850;
  font-variant-numeric: tabular-nums;
}

.comparison-meter__dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
}

.comparison-meter__dot.is-actual { background: var(--comparison-color); }

.comparison-meter__dot.is-benchmark {
  border: 1.5px solid var(--text-primary);
  background: var(--card-bg, var(--card));
}

.comparison-meter.is-empty {
  min-height: 50px;
  align-content: center;
}

.comparison-meter__empty {
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  font-weight: 700;
}

.comparison-meter.is-compact { gap: 5px; min-width: 0; }

.comparison-meter.is-compact .comparison-meter__legend { gap: 9px; }

@media (prefers-reduced-motion: reduce) {
  .comparison-meter__fill { transition: none; }
}

/* ===== Analytics: unified inventory + materials ======================= */

.inventory-insights {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.inventory-insights-filter {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  padding: 14px;
}

.inventory-insights-filter label { display: grid; gap: var(--space-1); }

.inventory-insights-filter p {
  margin: 0;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  line-height: 1.65;
}

.inventory-insights-notice {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-card);
}

.inventory-insights-notice.is-danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 26%, var(--border));
  background: color-mix(in srgb, var(--danger-soft) 72%, var(--bg-card));
}

.inventory-insights-notice.is-info {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 24%, var(--border));
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-card));
}

.inventory-insights-notice > div {
  display: grid;
  flex: 1;
  gap: 2px;
}

.inventory-insights-notice strong { font-size: var(--text-meta); }

.inventory-insights-notice span {
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.inventory-insights-table-card { overflow: visible; }

.inventory-insights-table td { vertical-align: middle; }

/* W-INVA (2026-07-24): 6 columns - name, consumption, stocktake, price, decision, impact. */

.inventory-insights-table td:nth-child(1) { width: 22%; }

.inventory-insights-table td:nth-child(2) { width: 14%; }

.inventory-insights-table td:nth-child(3) { width: 14%; }

.inventory-insights-table td:nth-child(4) { width: 15%; }

.inventory-insights-table td:nth-child(5) { width: 18%; }

.inventory-insights-table td:nth-child(6) { width: 17%; }

/* W-INVA: glanceable number cells in the analysis table (was detail-modal-only). */

.inv-insight-num {
  display: flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
}

.inv-insight-num strong {
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 850;
  font-variant-numeric: tabular-nums;
}

.inv-insight-num__sub {
  color: var(--text-tertiary);
  font-size: 10.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.inv-insight-num.tone-danger strong  { color: var(--danger); }

.inv-insight-num.tone-warning strong { color: var(--warning); }

.inv-insight-num.tone-success strong { color: var(--success); }

.inv-insight-num.tone-info strong    { color: var(--text-secondary); }

.inv-insight-num.tone-danger .inv-insight-num__sub,
.inv-insight-num.tone-success .inv-insight-num__sub { color: currentColor; opacity: 0.85; }

.inventory-insight-mobile__nums {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.inv-insight-mininum {
  display: grid;
  gap: 2px;
}

.inv-insight-mininum__k {
  color: var(--text-tertiary);
  font-size: 9.5px;
  font-weight: 750;
  letter-spacing: 0.02em;
}

.inventory-material-identity {
  display: grid;
  min-width: 0;
  gap: 3px;
}

.inventory-material-identity strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 850;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inventory-material-identity span {
  overflow: hidden;
  color: var(--text-tertiary);
  font-size: var(--text-micro);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inventory-insight-empty {
  color: var(--text-tertiary);
  font-size: 10.5px;
  font-weight: 650;
}

.inventory-insight-mobile {
  display: grid;
  gap: 13px;
}

.inventory-insight-mobile__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
}

.inventory-insight-mobile__head .inventory-material-identity { flex: 1; }

.inventory-insight-reason,
.inventory-insight-mobile__reason {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: 1.65;
}

.inventory-insight-mobile__reason {
  margin: 0;
}

.inventory-insight-mobile__impact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.inventory-insight-impact-value {
  font-size: var(--text-meta);
  font-weight: 900;
  white-space: nowrap;
}

.inventory-insight-impact-value.is-loss { color: var(--danger); }

.inventory-insight-impact-value.is-saving { color: var(--success); }

.inventory-insight-impact-value.is-price { color: var(--warning); }

.inventory-insight-detail {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.inventory-insight-decision {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-subtle);
}

.inventory-insight-decision > span:last-child {
  color: var(--text-secondary);
  font-size: var(--text-meta);
  line-height: 1.6;
}

.inventory-insight-decision.is-danger { border-color: color-mix(in srgb, var(--danger) 28%, var(--border)); }

.inventory-insight-decision.is-warning { border-color: color-mix(in srgb, var(--warning) 32%, var(--border)); }

.inventory-insight-decision.is-success { border-color: color-mix(in srgb, var(--success) 26%, var(--border)); }

.inventory-insight-detail__meters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}

.inventory-insight-detail__meters > * {
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-subtle);
}

.inventory-insight-impact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: var(--text-meta);
}

.inventory-insight-impact strong { color: var(--danger); font-size: var(--text-md); }

.inventory-insight-exclude { align-self: flex-start; }

@media (max-width: 600px) {
  .inventory-insights { gap: var(--space-3); }
  .inventory-insight-detail__meters { grid-template-columns: 1fr; }
  .inventory-insights-notice { align-items: flex-start; flex-wrap: wrap; }
  .inventory-insights-notice .btn { margin-inline-start: auto; }
}

/* Central premium free-text autofill for patient profile attributes. */

.smart-combobox { position: relative; }

.smart-combobox-menu {
  position: absolute;
  inset-inline-start: 0;
  inset-inline-end: 0;
  top: calc(100% + 4px);
  z-index: 30;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.16);
  max-height: 260px;
  overflow-y: auto;
  padding: var(--space-1);
}

.smart-combobox-hint {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  padding: 6px 10px;
  font-weight: 700;
  border-bottom: 1px dashed var(--border);
  margin-bottom: 4px;
}

.smart-combobox-option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  text-align: start;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-primary);
  transition: background 120ms ease;
}

.smart-combobox-option > svg { color: var(--text-tertiary); flex: 0 0 auto; }

.smart-combobox-option:hover,
.smart-combobox-option:focus-visible,
.smart-combobox-option.is-active {
  background: var(--accent-soft, color-mix(in srgb, var(--accent) 8%, transparent));
  outline: none;
}

/* ── Data Import tab (انقل عيادتك إلى Modirify) — ADR-231 / W0 ─────────── */

.import-tab { display: block; }

.import-head { margin-bottom: 18px; }

.import-paste-label { display: block; font-weight: 700; font-size: var(--text-sm); margin-bottom: 8px; }

.import-paste {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 180px;
  padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text-primary); font-size: var(--text-sm); line-height: 1.7;
  font-family: inherit;
}

.import-paste:focus-visible { outline: none; border-color: var(--accent); }

.import-actions {
  display: flex; gap: var(--space-2-5); justify-content: space-between; flex-wrap: wrap; margin-top: 16px;
}

.import-actions .btn { display: inline-flex; align-items: center; gap: var(--space-1-5); }

.import-warn {
  margin: 14px 0; padding: 10px 14px; border-radius: var(--radius);
  background: var(--warning-soft); color: var(--warning); font-size: var(--text-sm); font-weight: 600;
}

.import-map {
  margin: 16px 0; padding: var(--space-4); border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--bg-subtle, var(--surface));
}

.import-map-title { font-weight: 800; font-size: var(--text-base); margin-bottom: 4px; }

.import-map-hint { font-size: var(--text-meta); color: var(--text-secondary); margin-bottom: 14px; }

.import-map-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-2-5); }

.import-map-row {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}

.import-map-src {
  flex: 1 1 auto; min-width: 0; font-weight: 700; font-size: var(--text-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.import-map-arrow { color: var(--text-tertiary); flex: 0 0 auto; }

.import-map-select {
  flex: 0 0 auto; max-width: 50%; padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface); color: var(--text-primary);
  font-size: var(--text-sm); font-family: inherit;
}

.import-review { margin: 8px 0; }

.import-cell-empty { color: var(--text-tertiary); }

.import-tab tr.import-row-error td,
.import-tab .dt-card.import-row-error { background: var(--danger-soft); }

.import-tab tr.import-row-dup td,
.import-tab .dt-card.import-row-dup { background: var(--warning-soft); }

.import-offline { text-align: center; padding: 48px 16px; }

.import-offline-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 50%; margin-bottom: 14px;
  background: var(--bg-subtle, var(--surface)); color: var(--text-tertiary);
}

/* W1 live-import additions */

.import-resume {
  margin-bottom: 16px; padding: 12px 14px; border-radius: var(--radius);
  background: var(--accent-soft, color-mix(in srgb, var(--accent) 8%, transparent));
  border: 1px solid var(--accent);
}

.import-resume .import-actions { margin-top: 10px; }

.import-ai-busy {
  margin: 0 0 12px; padding: 8px 12px; border-radius: var(--radius);
  background: var(--accent-soft, color-mix(in srgb, var(--accent) 8%, transparent));
  color: var(--accent); font-size: 12.5px; font-weight: 700;
}

.import-controls { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; margin: 4px 0 12px; }

.import-check { display: inline-flex; align-items: center; gap: var(--space-1-5); font-size: var(--text-sm); cursor: pointer; }

.import-date-flip { display: inline-flex; align-items: center; gap: var(--space-2); font-size: 12.5px; color: var(--warning); }

.import-cell-amb { color: var(--warning); font-weight: 700; }

.import-progress-wrap { padding: 28px 8px; text-align: center; }

.import-progress { height: 12px; border-radius: var(--radius-pill); background: var(--bg-subtle, var(--border)); overflow: hidden; }

.import-progress-bar { height: 100%; background: var(--accent); transition: width 200ms ease; }

.import-summary-card {
  text-align: center; padding: 28px 16px; border-radius: var(--radius);
  background: var(--success-soft); margin-bottom: 16px;
}

.import-summary-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: 50%; margin-bottom: 12px;
  background: var(--success); color: #fff;
}

.import-preview-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--space-2-5); }

.import-preview-card { padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }

.import-preview-name { font-weight: 800; font-size: var(--text-base); margin-bottom: 4px; }

.import-preview-meta { font-size: 12.5px; color: var(--text-secondary); }

.import-preview-notes { margin-top: 6px; font-size: var(--text-meta); color: var(--text-tertiary); white-space: pre-line; }

/* "بياناتك" tab import/export toggle now renders via the shared
   ui/segmented-control.jsx primitive (Wave 7) - the .data-portability-seg /
   .data-seg-btn rules were removed as dead CSS. */

/* ── Data import MAGIC redesign (ADR-231 v2, W-MAGIC-1) ── */

.import-head { margin-bottom: 16px; }

.import-drop {
  width: 100%; display: flex; flex-direction: column; align-items: center; gap: var(--space-1-5);
  padding: 30px 16px; border: 1.5px dashed var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text-secondary); cursor: pointer; font-family: inherit;
}

.import-drop:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }

.import-drop:disabled { opacity: 0.6; cursor: default; }

.import-drop-title { font-weight: 800; font-size: var(--text-md); color: var(--text); }

.import-drop-hint { font-size: 12.5px; color: var(--text-tertiary); text-align: center; }

.import-sheets { margin: 14px 0; display: flex; flex-direction: column; gap: var(--space-2); }

.import-sheet-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2-5); flex-wrap: wrap;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}

.import-sheet-name { font-size: var(--text-sm); color: var(--text); display: inline-flex; align-items: center; gap: var(--space-1); min-width: 0; }

.import-sheet-count { color: var(--text-tertiary); font-size: var(--text-meta); }

.import-sheet-ctrl { display: inline-flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.import-tiles { display: flex; flex-wrap: wrap; gap: var(--space-2-5); margin: 4px 0 14px; }

/* .import-tile/-num/-label removed (KpiCard P5): count tiles live on MetricTile
   (.import-tiles wrapper + .import-summary-card stay - still live). */

.import-group { margin: 16px 0; padding-top: 12px; border-top: 1px solid var(--border); }

.import-group:first-of-type { border-top: none; padding-top: 0; }

.import-group-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 8px; }

.import-group-count { margin-inline-start: auto; font-weight: 800; color: var(--accent); }

.import-ledger {
  padding: 10px 14px; border-radius: var(--radius); background: var(--info-soft, var(--surface));
  color: var(--text); font-size: var(--text-sm); margin-bottom: 12px; line-height: 1.7;
}

.import-ledger.ok { background: var(--success-soft); }

.import-note { padding: 8px 12px; border-radius: var(--radius); background: var(--bg-subtle, var(--surface)); color: var(--text-secondary); font-size: 12.5px; margin: 10px 0; }

.import-details { margin: 12px 0; }

.import-details > summary { cursor: pointer; font-size: var(--text-sm); font-weight: 700; color: var(--text-secondary); padding: 6px 0; }

.import-details[open] > summary { color: var(--accent); margin-bottom: 8px; }

/* ===== Online booking W4 — reception requests tile + tray + confirm modal ===== */

/* The "طلبات الحجز" KPI tile pulses to draw the eye (the clinic has no
   notification bell). A soft accent ring, not an opacity blink (which reads
   as "loading"). */

@keyframes kpiPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(14, 165, 183, 0.0); }
  50%      { box-shadow: 0 0 0 4px var(--accent-soft, rgba(14, 165, 183, 0.25)); }
}

.kpi-pulse { animation: kpiPulse 1.8s ease-in-out infinite; border-radius: var(--radius-lg, 14px); }

.kpi-pulse:hover { cursor: pointer; }

/* Confirm modal (§2.9 identity one-tap) */

.bkc-review {
  background: var(--bg-subtle, var(--surface-2, rgba(0,0,0,0.03)));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 14px;
}

.bkc-review-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  color: var(--text-primary);
  font-size: 12.5px;
  margin-block-end: 3px;
}

.bkc-review-row { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--text-secondary); }

.bkc-review-row > .bkc-review-ico { width: 20px; text-align: center; flex: 0 0 auto; }

.bkc-review-note { color: var(--text-tertiary); font-size: 12.5px; white-space: pre-wrap; word-break: break-word; }

.bkc-form-intro { font-size: var(--text-sm); font-weight: 700; color: var(--text-secondary); margin-bottom: 10px; }

.bkc-form { display: flex; flex-direction: column; gap: var(--space-2-5); }

.bkc-choose { display: flex; flex-direction: column; gap: var(--space-2-5); }

.bkc-cands { display: flex; flex-direction: column; gap: var(--space-2); }

.bkc-cand {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2-5);
  border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px;
}

.bkc-cand-name { font-weight: 700; font-size: var(--text-base); color: var(--text-primary); }

.bkc-cand-sub { font-size: var(--text-meta); color: var(--text-tertiary); margin-top: 2px; }

.bkc-new-btn { margin-top: 4px; }

/* CHECKIN_AUDIT Wave B: same-day-appointment soft warning (B-12 candidate cue +
   #15 walk-in banner). Non-blocking amber, mirrors the wizard multi-book warning. */

.bkc-cand-warn { font-size: var(--text-meta); color: var(--warning, #b45309); font-weight: 600; margin-top: 3px; }

/* Reception modal contract — one visual/interaction grammar for booking,
   walk-in, and online-request flows. Logical properties keep RTL/LTR aligned. */

.rm-card.reception-modal {
  --rm-max-width: 640px;
}

.reception-modal-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.patient-form-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.patient-form-fields input.input,
.patient-form-fields select.input,
.patient-form-fields .smart-combobox > input.input {
  height: 42px;
  block-size: 42px;
  min-block-size: 42px;
  padding-block: 0;
}

.patient-form-optional {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
  overflow: visible;
}

.patient-form-optional > summary {
  min-height: 42px;
  padding: 10px 12px;
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  list-style: none;
  color: var(--text-secondary);
  font-size: 12.5px;
  font-weight: 800;
}

.patient-form-optional > summary::-webkit-details-marker { display: none; }

.patient-form-optional > summary span {
  margin-inline-start: auto;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  font-weight: 600;
}

.patient-form-optional[open] > summary {
  border-block-end: 1px solid var(--border);
}

.patient-form-optional[open] > summary svg { transform: rotate(45deg); }

.patient-form-optional__body {
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.patient-form-booking-confirm .patient-form-fields { gap: 14px; }

/* Shared items-section header: new-invoice's line items + the purchase
   items editor (Anatomy W4) — one selector so the two cannot drift. */

.new-invoice-form .nif-items-header,
.purchase-items-editor .nif-items-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.invoice-editor-form__section-title {
  color: var(--text-primary);
  font-size: var(--text-base);
  font-weight: 800;
}

.invoice-editor-form__items {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-block-end: 12px;
}

.invoice-editor-form__empty {
  padding: var(--space-5);
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  text-align: center;
}

.invoice-editor-form__readonly {
  display: flex;
  align-items: center;
  min-height: var(--field-h);
  background: var(--bg-subtle);
  color: var(--text-primary);
  font-weight: 700;
}

.session-form__invoice-context {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.session-form__invoice-owner {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.session-form__invoice-owner strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: 13.5px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-form__invoice-owner span {
  overflow: hidden;
  color: var(--text-tertiary);
  font-size: 11.5px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-form__invoice-context > span {
  font-size: 12.5px;
  font-weight: 800;
  white-space: nowrap;
}

.session-form__invoice-context .is-due { color: var(--danger); }

.session-form__invoice-context .is-settled { color: var(--success); }

.reception-time-range {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

.reception-time-range__part {
  min-width: 0;
  position: relative;
}

.reception-time-range__part > span {
  position: absolute;
  z-index: 1;
  inset-block-start: 5px;
  inset-inline-start: 10px;
  color: var(--text-tertiary);
  font-size: 9.5px;
  font-weight: 700;
  pointer-events: none;
}

.reception-time-range__part .input {
  min-width: 0;
  padding-block-start: 13px;
  font-size: var(--text-meta);
}

.reception-time-range__duration {
  color: var(--accent);
  font-size: 10.5px;
  font-weight: 800;
  white-space: nowrap;
}

.session-form__progress {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.session-form__progress-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 800;
}

.session-form__optional { margin-block-start: 0; }

.invoice-form-optional { margin-block-start: 2px; }

.reception-modal-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 0;
}

.reception-modal-field__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
}

.reception-modal-field__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--text-secondary);
  font-size: 12.5px;
  font-weight: 800;
}

.reception-modal-field__required { color: var(--danger); }

.reception-modal-field__action {
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
  white-space: nowrap;
}

.reception-modal-field__action:hover { text-decoration: underline; }

.reception-choice-options {
  display: grid;
  grid-template-columns: repeat(var(--reception-choice-columns, 2), minmax(0, 1fr));
  gap: var(--space-2-5);
}

.reception-choice-option {
  min-height: 52px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-elevated);
  color: var(--text-primary);
  padding: 11px 13px;
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  text-align: start;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, box-shadow 150ms ease;
}

.reception-choice-option:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }

.reception-choice-option.is-selected {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
}

.reception-choice-option__check {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  display: grid;
  place-items: center;
  color: var(--accent);
  flex: 0 0 auto;
}

.reception-choice-option.is-selected .reception-choice-option__check {
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.reception-choice-option__copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.reception-choice-option__copy strong { font-size: 13.5px; }

.reception-choice-option__copy > span {
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: 10.5px;
  line-height: 1.5;
}

.reception-doctor-select { min-height: 44px; }

.reception-modal-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

.reception-context-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
  padding: 12px 14px;
  color: var(--text-secondary);
  font-size: 12.5px;
  line-height: 1.65;
}

.reception-context-card.is-warning {
  border-color: color-mix(in srgb, var(--warning, #f59e0b) 42%, var(--border));
  background: var(--warning-soft, #fff7ed);
  color: var(--text-primary);
}

.reception-context-card__label {
  color: var(--text-tertiary);
  font-size: 11.5px;
  font-weight: 800;
  margin-block-end: 6px;
}

.reception-context-card__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1-5);
  margin-block: 6px;
}

.reception-context-card__chip {
  display: inline-flex;
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-size: var(--text-xs);
  font-weight: 700;
}

.reception-modal-details {
  border-top: 1px dashed var(--border);
  padding-block-start: 12px;
}

.reception-modal-details > summary {
  color: var(--text-secondary);
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
  list-style-position: inside;
}

.reception-modal-details[open] > summary { margin-block-end: 9px; }

.reception-modal-footer {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.reception-modal-footer__summary {
  flex: 1 1 auto;
  min-width: 0;
}

.reception-modal-summary {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.reception-modal-summary__label {
  color: var(--text-tertiary);
  font-size: 10.5px;
  font-weight: 700;
}

.reception-modal-summary__primary,
.reception-modal-summary__secondary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reception-modal-summary__primary { color: var(--text-primary); font-size: 12.5px; }

.reception-modal-summary__secondary { color: var(--text-secondary); font-size: 11.5px; }

.reception-modal-footer__actions {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 0 0 auto;
}

.reception-modal-footer__actions .btn { flex: 0 0 auto; min-width: 92px; }

/* Anatomy W4: optional destructive slot (edit modals) — sits at the START
   of the footer, before the summary spacer, far from the primary action. */

.reception-modal-footer__destructive { flex: 0 0 auto; min-width: 0; }

.reception-form-page {
  max-width: 680px;
  margin-inline: auto;
}

.reception-form-page__footer {
  border-top: 1px solid var(--border);
  margin: 20px -24px -24px;
  padding: 16px 24px;
  background: var(--bg-subtle);
}

@media (max-width: 47.99em) {
  .reception-modal-form { gap: 15px; }
  .reception-modal-grid { grid-template-columns: 1fr; }
  .reception-choice-option { min-height: 50px; padding: var(--space-2-5); }
  .reception-modal-footer {
    align-items: stretch;
    flex-direction: column;
    gap: 9px;
  }
  .reception-modal-footer__actions { width: 100%; }
  .reception-modal-footer__actions .btn { flex: 1 1 0; min-width: 0; }
  .reception-modal-summary { text-align: start; }
  .reception-form-page__footer { margin-inline: -16px; padding-inline: 16px; }
  .patient-form-fields { gap: 14px; }
  .reception-time-range { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .new-invoice-form .nif-items-header .btn,
  .purchase-items-editor .nif-items-header .btn {
    width: 100%;
    justify-content: center;
  }
  /* Edit-modal delete keeps a compact width when the footer stacks. */
  .reception-modal-footer__destructive { align-self: flex-start; }
}

@media (max-width: 24em) {
  .reception-choice-options { grid-template-columns: 1fr; }
}

/* =====================================================================
   Online booking — settings tab (ADR-232 W5)
   ===================================================================== */

/* .booking-set* removed 2026-07-28 (settings unification): the enable row is a
   SettingSwitch, the plan-gate and non-owner notes are SettingsNote, and the
   hint is .set-hint — one settings vocabulary instead of a booking-only one.
   .booking-share* + .booking-qr-poster* below stay: the printable QR poster is
   genuinely its own object, not a settings shape. */

/* Share section: public link + printable QR poster */

.booking-share { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: var(--space-5); }

.booking-link-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.booking-link-row .input { flex: 1 1 240px; min-width: 0; font-size: 12.5px; }

.booking-link-row .btn { flex: 0 0 auto; }

/* .booking-qr-block removed 2026-07-13 - the poster is a direct child of
   .booking-share now (QR on top, link + actions below). */

.booking-qr-poster {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2-5);
  padding: 24px 28px; border: 1px solid var(--border); border-radius: var(--radius-lg, 14px);
  background: #ffffff; max-width: 440px; width: 100%; margin-inline: auto;
}

.booking-qr-poster-clinic { font-weight: 800; font-size: 18px; color: #0f172a; text-align: center; }

.booking-qr-poster-head { font-size: var(--text-base); color: #475569; font-weight: 700; }

.booking-qr-poster-qr { padding: var(--space-2); background: #fff; border-radius: var(--radius-md); }

.booking-qr-poster-scan { font-size: 12.5px; color: #64748b; }

/* .booking-qr-poster-url removed 2026-07-13 (owner: it duplicated the link field). */

/* W6: print + download actions row under the QR poster */

.booking-qr-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: center; }

/* Booking tab: appointments-style split (owner 2026-07-13 - SAME proportions as
   .appt-split). The share/QR card is the WIDE column on the RTL-right (first track,
   1.3fr) like the calendar; the requests queue the NARROW column on the RTL-left
   (.95fr) like the agenda. Stacks on a phone. */

.booking-split {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, .95fr);
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 900px) { .booking-split { grid-template-columns: minmax(0, 1fr); } }

.booking-req-card { overflow: hidden; }

.booking-req-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: 16px 18px; border-bottom: 1px solid var(--border);
}

.booking-req-title { font-size: 17px; font-weight: 800; color: var(--text-primary); }

.booking-req-sub { font-size: var(--text-meta); color: var(--text-tertiary); margin-top: 2px; }

.booking-req-list { display: flex; flex-direction: column; }

.booking-req-row {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: 13px 18px; border-bottom: 1px solid var(--border);
}

.booking-req-row:last-child { border-bottom: none; }

.booking-req-row:hover { background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated)); }

.booking-req-main { flex: 1 1 240px; min-width: 0; }

.booking-req-name {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-weight: 800; font-size: var(--text-md); color: var(--text-primary);
}

.booking-req-match { font-size: 10.5px; font-weight: 700; padding: 2px 8px; }

.booking-req-meta {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 5px;
  font-size: 12.5px; color: var(--text-secondary);
}

.booking-req-chip { display: inline-flex; align-items: center; gap: var(--space-1); }

.booking-req-note {
  margin-top: 7px; font-size: var(--text-meta); color: var(--text-secondary);
  background: var(--bg-subtle); border-radius: var(--radius-md); padding: 7px 10px;
}

.booking-req-actions { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-2); }

.booking-req-decline { color: var(--danger); }

@media (max-width: 560px) {
  .booking-req-actions { width: 100%; }
  .booking-req-actions .btn { flex: 1; }
}

/* =====================================================================
   Online booking — the two panels (ADR-232 W6)
   The reception "طلبات الحجز" panel and the settings enable card BOTH render
   through SectionCard since 2026-07-28, so the booking-cfg-* and booking-enable-*
   rules are gone: they were a private copy of a head this app already had, and
   the copy is why the panel looked like a transplant next to whatever it sat
   beside. The share block above still styles the body.
   ===================================================================== */

.set-section > .booking-share { margin-top: 0; padding-top: 0; border-top: none; }

.chip.neutral { background: var(--bg-subtle); color: var(--text-tertiary); }

/* Print: isolate the QR poster only.
   Unify W1 (2026-07-07): scoped to body.printing-qr (set by booking-config
   doPrint() only). The unscoped `body *{visibility:hidden}` used to fire on
   EVERY window.print() and blank the x-ray lightbox + analytics prints. */

@media print {
  body.printing-qr * { visibility: hidden !important; }
  body.printing-qr .booking-qr-poster,
  body.printing-qr .booking-qr-poster * { visibility: visible !important; }
  body.printing-qr .booking-qr-poster {
    position: fixed; inset: 0; margin: auto; border: none; box-shadow: none;
    width: auto; height: max-content; max-width: none;
  }
  body.printing-qr .booking-print-btn { display: none !important; }
}

/* ──────────────────────────────────────────────────────────────────────
   Mobile app native-feel polish (Capacitor). SCOPED to body.is-native
   (the class native-bridge.js adds only inside the installed app), so the
   website is 100% unaffected. Kills the "website in a box" tells:
     - tap-highlight flash on every touch
     - long-press text-selection / iOS callout on chrome (buttons, nav)
     - browser pull-to-refresh + overscroll glow (also stops an accidental
       reload that would drop unsaved work)
     - the 300ms double-tap-zoom delay on interactive controls
   user-select:none is scoped to chrome ONLY — content surfaces (patient
   notes, invoices, phone numbers) stay selectable/copyable.
   ────────────────────────────────────────────────────────────────────── */

body.is-native {
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

body.is-native .btn,
body.is-native .icon-btn,
body.is-native .nav-item,
body.is-native .tab,
body.is-native .topbar,
body.is-native .topbar-actions,
body.is-native .mobile-nav,
body.is-native .sidebar,
body.is-native button {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

/* Long-press quick-actions: a press-and-hold on a card is OUR gesture
   (lib/long-press.js), not a text-select — so on touch devices suppress the
   native selection highlight + iOS copy/paste callout that the hold would
   otherwise raise (owner report 2026-07-06). Two targets:
     - [data-longpress-id]: the pressed card itself.
     - .lp-quick-sheet (+ its descendants): the ResponsiveModal that OPENS on the
       hold. It's a body portal (NOT a card descendant), so the finger landing on
       its TITLE could still trigger Android's Copy/Translate/Select-all bar
       (owner screenshot). Suppressing selection on the whole action sheet kills it.
   Scoped to coarse pointers so desktop keeps the name/phone selectable. */

@media (pointer: coarse) {
  [data-longpress-id],
  .lp-quick-sheet,
  .lp-quick-sheet * {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
}

/* Breathing room under the reserved status bar. With overlaysWebView:false
   the toolbar/header sat flush against the status-bar strip and felt jammed
   ("التوب بار طالع فوق اوي"). Push the top content down a comfortable gap.
   App only (body.is-native); the website is untouched. The safe-area var is
   a 0-cost bonus if the status bar ever switches back to overlay mode. */

body.is-native .topbar {
  height: auto;
  min-height: 60px;
  padding-top: calc(28px + var(--safe-area-inset-top, env(safe-area-inset-top, 0px)));
  padding-bottom: 12px;
}

body.is-native .lp-nav-inner {
  padding-top: calc(30px + var(--safe-area-inset-top, env(safe-area-inset-top, 0px)));
}

/* ── Premium mobile app bar (topbar). App only (body.is-native) -> the
   website is untouched, and it ships free via OTA. Reported on the S24:
   the screen title truncated to a broken stub ("الاستقب...") and the bar
   felt cramped. Measured root cause (headless, real CSS, at the S24's
   360px CSS width): the four action icons are each floored to 44px for
   WCAG touch targets (intentional -- doctors were mis-tapping), so that
   cluster locks up ~182px of the ~332px inner width. The flex title block
   collapses to ~98px, and the 26px per-screen help button inside it
   leaves the name only ~64px -- less than "الاستقبال" needs (~82px). We do
   NOT shrink the touch targets. Instead we reclaim room the title's own
   way: 16px text (narrower), a slimmer 22px help button, a tighter title-
   row gap, and trimmed bar padding. That lands the common home titles
   ("الاستقبال" / "المواعيد" / "الفواتير") in full; genuinely long names
   still ellipsize, which the hero below restates anyway. The status-bar
   padding above owns the vertical breathing room; this owns horizontal. */

body.is-native .topbar {
  padding-inline: 10px;
  gap: var(--space-1-5);
}

/* Title row: 16px + a tighter internal gap (override the inline 8px). The
   :first-child guard keeps this off the subtitle / coach-bubble siblings,
   which carry their own inline font sizes. */

body.is-native .topbar-title-block > div:first-child {
  font-size: 16px !important;
  gap: 6px !important;
}

/* The per-screen help button lives in the title row and directly competes
   with the name; slim it 26 -> 22 so it stays discoverable without the
   crush. */

body.is-native .topbar-help-btn {
  width: 22px !important;
  height: 22px !important;
}

/* Mobile performance: continuous decorative motion repaints every frame inside
   the Android WebView. Keep functional loading skeletons animated, but pause
   the measured reception chrome loops in the installed app only. */

body.is-native .kanban-count,
body.is-native .kanban-empty,
body.is-native .pulse-soft,
body.is-native .kpi-pulse {
  animation: none !important;
}

body.is-native .kanban-count::after,
body.is-native .progress::after {
  animation: none !important;
  opacity: 0 !important;
}

body.is-native .ai-ball__pulse {
  display: none !important;
  animation: none !important;
}

/* ============================================================
   Appointments tab v2 - month calendar + day agenda
   (owner-locked redesign 2026-07-12). Namespaced .appt-cal-* /
   .appt-agenda-* so it never disturbs the shared .calendar-grid.
   Theme-tokenized (no fixed turquoise); six statuses read the
   shared APPT_STATUS_TONE via inline var() from the component.
   ============================================================ */

.appt-split {
  display: grid;
  /* minmax(0, ...) NOT bare fr: a bare `1fr` track has min-width:auto and
     REFUSES to shrink below its widest child's intrinsic width. The wide
     day-agenda row (present ONLY on days with appointments) forced the column -
     and the whole grid - past a ~430px phone, so the reception overflow:auto
     wrapper scrolled horizontally. minmax(0, ...) lets the columns shrink to
     the container. ROOT fix, confirmed by measuring the live DOM (2026-07-13). */
  grid-template-columns: minmax(0, 1.3fr) minmax(0, .95fr);
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 780px) {
  .appt-split { grid-template-columns: minmax(0, 1fr); }
}

/* --- Insight results panel (W3-C, 2026-07-27) -------------------------
   TEMPORARY by design: it renders only while a card's rows are focused
   and leaves with the ✕. The tab in its daily life never sees it, which
   is why this adds no second permanent surface to maintain. Rows are
   grouped by date because an insight's set crosses months by nature. */

.appt-focus {
  border: 1px solid var(--accent);
  border-radius: var(--radius-lg, 12px);
  background: var(--bg-subtle);
  margin-bottom: var(--space-4);
  overflow: hidden;
}

.appt-focus__head {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--accent-soft);
}

.appt-focus__title { font-weight: 600; font-size: 13.5px; }

.appt-focus__count { font-size: 12px; color: var(--text-muted); }

.appt-focus__clear {
  margin-inline-start: auto;
  border: 0; background: transparent; cursor: pointer;
  font-size: 15px; line-height: 1; color: var(--text-muted);
  padding: 4px 6px; border-radius: 6px;
}

.appt-focus__clear:hover { background: var(--bg); color: var(--text); }

/* Capped + scrollable: a 200-row set must not push the calendar off screen. */

.appt-focus__body { max-height: 320px; overflow-y: auto; }

.appt-focus__empty {
  padding: 18px 14px; font-size: 13px; color: var(--text-tertiary);
  text-align: center;
}

.appt-focus__day {
  padding: 6px 14px; font-size: 11.5px; font-weight: 600;
  color: var(--text-muted); background: var(--bg);
  position: sticky; top: 0;
}

.appt-focus__row {
  display: grid;
  /* minmax(0, ...) not bare fr - the same intrinsic-width trap the split
     above documents; a long patient name would force a horizontal scroll. */
  grid-template-columns: 56px minmax(0, 1.4fr) minmax(0, 1fr) auto;
  gap: 8px; align-items: center; width: 100%;
  padding: 9px 14px; border: 0; border-top: 1px solid var(--border);
  background: transparent; cursor: pointer; text-align: start;
  font-size: 12.5px; color: var(--text);
}

.appt-focus__row:hover { background: var(--bg); }

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

.appt-focus__patient { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.appt-focus__doctor { color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.appt-focus__status {
  font-size: 11px; padding: 2px 8px; border-radius: var(--radius-pill);
  border: 1px solid currentColor; white-space: nowrap;
}

@media (max-width: 560px) {
  .appt-focus__row { grid-template-columns: 48px minmax(0, 1fr) auto; }
  .appt-focus__doctor { display: none; }
}

/* The doctor filter returned to the appointments header (W3-C): it was
   built, wired to the server hook and both views, then lost its control
   in the Unify W8 hero cleanup. Sized to sit beside the DateControl. */

.appt-doctor-filter { max-width: 170px; height: 34px; padding-block: 0; font-size: 12.5px; }

/* --- Month calendar card --- */

.appt-cal-card { padding: 18px; }

.appt-cal-head { margin-bottom: 12px; }

.appt-cal-title { display: flex; flex-direction: column; align-items: center; line-height: 1.25; }

.appt-cal-title-main { font-size: 18px; font-weight: 800; letter-spacing: -.3px; color: var(--text-primary); }

.appt-cal-title-sub { font-size: var(--text-meta); font-weight: 500; color: var(--text-tertiary); margin-top: 2px; }

.appt-cal-weekdays,
.appt-cal-grid {
  display: grid;
  /* minmax(0, 1fr) per track (not bare 1fr) so the 7 columns can shrink on a
     phone instead of the cells' intrinsic width forcing the calendar card past
     the viewport - same root grid-shrink fix as .appt-split above. */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-1-5);
}

.appt-cal-weekdays { margin-bottom: 6px; }

.appt-cal-weekday {
  text-align: center;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-tertiary);
  padding: 4px 0;
}

.appt-cal-cell {
  min-height: 56px;
  border-radius: var(--radius);
  padding: 7px 9px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  transition: filter var(--duration-fast, .15s) var(--ease-smooth, ease),
              border-color var(--duration-fast, .15s) var(--ease-smooth, ease);
}

.appt-cal-cell.is-blank {
  border-color: transparent;
  background: transparent;
  cursor: default;
  pointer-events: none;
}

.appt-cal-cell:not(.is-blank):hover { filter: brightness(.98); border-color: var(--accent); }

.appt-cal-cell.is-today {
  background: var(--accent-soft);
  border-color: var(--accent);
}

.appt-cal-cell.is-selected {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 8px 18px -10px color-mix(in srgb, var(--accent) 70%, transparent);
}

.appt-cal-daynum { font-size: 13.5px; font-weight: 600; color: var(--text-primary); }

.appt-cal-cell.is-today .appt-cal-daynum { color: var(--accent-text, var(--accent)); font-weight: 700; }

.appt-cal-cell.is-selected .appt-cal-daynum { color: #fff; font-weight: 700; }

.appt-cal-dots { display: flex; gap: 3px; margin-top: auto; padding-top: 6px; }

.appt-cal-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }

.appt-cal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--text-secondary);
}

.appt-cal-legend-item { display: inline-flex; align-items: center; gap: 5px; }

/* --- Day agenda card --- */

/* overflow must stay VISIBLE: the per-row RowActionMenu (kebab) dropdown is an
   absolutely-positioned child that drops BELOW the row - overflow:hidden here
   clipped it so the menu never showed. The card corners stay clean without
   clipping (no child background reaches the rounded corners). */

.appt-agenda-card { overflow: visible; }

.appt-agenda-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}

.appt-agenda-head-main {
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.appt-agenda-head > .seg-control { flex: 0 0 auto; }

.appt-agenda-title { font-size: var(--text-md); font-weight: 700; color: var(--text-primary); }

.appt-agenda-badge {
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--accent-text, var(--accent));
  background: var(--accent-soft);
  padding: 4px 10px;
  border-radius: var(--radius-xl);
  white-space: nowrap;
}

.appt-agenda-badge.is-empty { color: var(--text-tertiary); background: var(--bg-subtle); }

.appt-now-sep {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 6px 20px;
  background: var(--accent-soft);
}

.appt-now-label { font-size: var(--text-xs); font-weight: 700; color: var(--accent-text, var(--accent)); white-space: nowrap; }

.appt-now-line { flex: 1; height: 1px; background: var(--accent); opacity: .3; }

.appt-agenda-list { display: flex; flex-direction: column; }

/* Redesigned 2026-07-13 (owner). The agenda card is a query CONTAINER so the row
   goes COMPACT single-line when the column is wide (desktop) and STACKS (name line
   over meta line) when narrow (phone) - keyed to the real column width, which
   depends on the split being 1 or 2 columns, NOT the viewport. Same responsive idea
   as the central DataTable (table on desktop / cards on phone), width-driven. */

.appt-agenda-card { container-type: inline-size; container-name: apptagenda; }

/* Booking requests reuse their existing rows inside the day-agenda card. The
   wrapper deliberately adds no second card/header: one shell, one switcher,
   one content area. */

.booking-requests-agenda,
.booking-requests-agenda__body,
.booking-req-card--agenda { min-width: 0; }

.booking-req-card--agenda { overflow: visible; }

.appt-agenda-row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 9px 16px;
  border-inline-start: 3px solid transparent;   /* status tone set inline */
  border-bottom: 1px solid var(--border);
}

.appt-agenda-row:last-child { border-bottom: none; }

.appt-agenda-row.is-live { background: color-mix(in srgb, var(--accent) 6%, var(--bg-elevated)); }

/* Only journey rows are clickable, so only they get the hover cue (no chevron -
   the WHOLE ROW toggles the visit journey now, owner 2026-07-13). */

.appt-agenda-row.is-expandable:hover { background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated)); }

/* Dim inactive (cancelled/no_show) via the CONTENT cells, NOT the row box: opacity<1
   on the row makes a stacking context that traps the open kebab dropdown beneath
   later rows (owner-caught 2026-07-12). Leave .appt-agenda-actions untouched. */

.appt-agenda-row.is-inactive .appt-agenda-time,
.appt-agenda-row.is-inactive .appt-agenda-body { opacity: .6; }

.appt-agenda-row.is-inactive .appt-agenda-name { text-decoration: line-through; }

.appt-agenda-time { flex: 0 0 42px; text-align: center; line-height: 1.15; padding-top: 1px; }

.appt-agenda-time-num { display: block; font-size: var(--text-base); font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text-primary); }

.appt-agenda-time-mer { display: block; font-size: var(--text-micro); color: var(--text-tertiary); margin-top: 1px; }

/* Avatar + expand-chevron removed 2026-07-13 (owner). Body = name over meta line;
   STACKED by default so a narrow phone never squeezes the name (it owns its line). */

.appt-agenda-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }

.appt-agenda-name {
  min-width: 0; line-height: 1.3;
  font-weight: 700; font-size: 13.5px; color: var(--text-primary);
  /* Wrap at WORD boundaries only - never a letter-per-line, never ellipsis. */
  overflow-wrap: break-word; word-break: normal; white-space: normal;
}

.appt-agenda-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 8px; min-width: 0; }

.appt-agenda-desc {
  flex: 0 1 auto; min-width: 0;
  font-size: 11.5px; color: var(--text-tertiary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.appt-agenda-pill {
  display: inline-flex; align-items: center;
  font-size: var(--text-xs); font-weight: 700;
  padding: 2px 9px; border-radius: var(--radius-xl);
  white-space: nowrap; flex: 0 0 auto;
}

.appt-live-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; margin-inline-end: 5px;
  animation: apptPulseDot 1.4s infinite;
}

.appt-agenda-actions { flex: 0 0 auto; display: inline-flex; align-items: center; }

/* Wide column (desktop): ONE compact line - name + meta inline, row centered. The
   name keeps min-width:0 so a rare very long name still wraps at words, never
   overflows. Below 480px (phone / small split) it stays stacked = name protected. */

@container apptagenda (min-width: 480px) {
  .appt-agenda-row { align-items: center; padding: 7px 16px; }
  .appt-agenda-body { flex-direction: row; align-items: center; gap: var(--space-2-5); }
  .appt-agenda-name { flex: 0 1 auto; }
  .appt-agenda-meta { flex: 1 1 auto; }
}

@container apptagenda (max-width: 430px) {
  .appt-agenda-head { align-items: stretch; flex-direction: column; }
  .appt-agenda-head > .seg-control { width: 100%; }
  .booking-req-card--agenda .booking-req-actions { width: 100%; }
  .booking-req-card--agenda .booking-req-actions .btn { flex: 1; }
}

.appt-agenda-empty { padding: var(--space-2); }

@keyframes apptPulseDot {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .45; transform: scale(.82); }
}

body.is-native .appt-live-dot { animation: none; }

/* --- Visit journey (owner 2026-07-12): the row's paid chip + expand chevron,
   and the expandable arrived -> seated -> left timeline strip. Collapsed by
   default; only "seated" rows are expandable. All theme-tokenized. --- */

.appt-agenda-row.is-expandable { cursor: pointer; }

.appt-agenda-row.is-open { background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated)); }

.appointment-payment-chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  flex: 0 0 auto; white-space: nowrap;
  font-size: 11.5px; font-weight: 700; color: var(--success);
  background: color-mix(in srgb, var(--success) 12%, var(--bg-elevated));
  border: 1px solid color-mix(in srgb, var(--success) 26%, var(--border));
  padding: 4px 9px; border-radius: var(--radius-xl); font-variant-numeric: tabular-nums;
}

.appointment-payment-chip strong{ font-size: inherit; font-weight: 900; }

.appointment-payment-chip.is-compact{ padding: 3px 8px; font-size: 10.5px; }

/* .appt-agenda-chev removed 2026-07-13 (owner): no expand chevron - the whole row
   toggles the visit journey (see renderRow onClick). */

.appt-jrny {
  padding: 14px 18px 16px;
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
}

.appt-jrny-track { display: flex; align-items: flex-start; overflow-x: auto; }

.appt-jrny-node {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-1);
  flex: 0 0 auto; width: 84px; text-align: center;
}

.appt-jrny-dot {
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--accent); background: var(--bg-elevated);
}

.appt-jrny-dot.is-filled { background: var(--accent); }

.appt-jrny-node b { font-size: var(--text-xs); font-weight: 600; color: var(--text-primary); }

.appt-jrny-node time { font-size: var(--text-meta); font-weight: 700; color: var(--text-primary); font-variant-numeric: tabular-nums; }

.appt-jrny-node time.is-muted { color: var(--text-tertiary); font-weight: 500; }

/* W-OPS: lateness against the booked slot. Warning tone, never danger - a late
   patient is a fact to plan around, not a failure to accuse anyone of. */

.appt-jrny-late {
  font-size: 10px; font-weight: 700; color: var(--warning);
  background: var(--warning-soft); padding: 1px 7px; border-radius: var(--radius-xl);
  white-space: nowrap;
}

.appt-jrny-seg { flex: 1; display: flex; flex-direction: column; align-items: center; padding-top: 5px; min-width: 38px; }

.appt-jrny-seg i { width: 100%; height: 2px; background: var(--accent); opacity: .35; border-radius: 2px; }

.appt-jrny-seg span {
  font-size: 10.5px; font-weight: 700; color: var(--accent-text, var(--accent));
  background: var(--accent-soft); padding: 1px 8px; border-radius: var(--radius-xl);
  margin-top: 6px; white-space: nowrap;
}

.appt-jrny-work { margin-top: 12px; font-size: var(--text-meta); color: var(--text-secondary); line-height: 1.5; }

.appt-jrny-work b { color: var(--text-primary); font-weight: 700; }

.appt-jrny-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--border); }

.appt-jrny-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; font-weight: 600; color: var(--text-secondary);
  background: var(--bg-elevated); border: 1px solid var(--border);
  padding: 4px 10px; border-radius: 10px; white-space: nowrap;
}

/* Shared entrance for expandable detail panels and SVG-adjacent overlays. */

@keyframes ttIn {
  from { opacity: 0; transform: scale(0.88) translateY(4px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* ══════════════════════════════════════════════════════════════════════
   ScreeningQuestion — the yes/no intake row (window.ReceptionModalUI.
   ScreeningQuestion). Health-history forms are answered by exception: a
   clinician confirms a short list of yes/no questions and only a "yes"
   unfolds its follow-up fields, so a patient with nothing to declare is a
   handful of taps instead of fifteen empty inputs (owner 2026-07-19).

   It reuses the reception modal's own selection language — the same border /
   accent-soft / ring treatment `.reception-choice-option.is-selected` wears — so
   it reads as part of the entry-modal family, not a new control.
   ══════════════════════════════════════════════════════════════════════ */

.screening-q {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-elevated);
  padding: 11px 13px;
  transition: border-color 150ms ease, background 150ms ease;
}

/* An UNANSWERED question stays visibly open business — it is «غير مسجّل», and
   the form's completeness counter reads it as such. */

.screening-q:not(.is-answered) { border-style: dashed; }

.screening-q.is-open {
  border-color: color-mix(in srgb, var(--danger) 32%, var(--border));
  background: color-mix(in srgb, var(--danger) 4%, var(--bg-elevated));
}

.screening-q__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.screening-q__label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text-primary);
  min-width: 0;
}

.screening-q__icon {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-md);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.screening-q.is-open .screening-q__icon {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  color: var(--danger);
}

.screening-q__answers { display: flex; gap: var(--space-1-5); flex-shrink: 0; }

.screening-q__answer {
  appearance: none;
  min-height: 36px;
  min-width: 58px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease, box-shadow 150ms ease;
}

.screening-q__answer:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }

.screening-q__answer.is-selected {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
}

/* A "yes" on a clinical risk is the one answer that must LOOK different. */

.screening-q__answer.is-selected.tone-danger {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  color: var(--danger);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 20%, transparent);
}

.screening-q__answer.is-selected.tone-warning {
  border-color: var(--warning);
  background: color-mix(in srgb, var(--warning) 12%, transparent);
  color: var(--warning);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--warning) 20%, transparent);
}

.screening-q__detail {
  margin-top: 11px;
  padding-top: 11px;
  border-top: 1px dashed color-mix(in srgb, var(--border) 85%, transparent);
  display: grid;
  gap: var(--space-2-5);
  animation: screening-unfold 180ms cubic-bezier(.16, 1, .3, 1) both;
}

@keyframes screening-unfold {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .screening-q,
  .screening-q__answer { transition: none; }
  .screening-q__detail { animation: none; }
}

@media (max-width: 480px) {
  /* On a phone the answers drop to their own full-width row so the label keeps
     its line and both pills stay comfortably tappable. */
  .screening-q__answers { width: 100%; }
  .screening-q__answer { flex: 1 1 0; min-height: 40px; }
}

/* The infectious-disease detail: three checkboxes on one wrapping row. */

.screening-q__viruses { display: flex; flex-wrap: wrap; gap: 14px; }

.screening-q__virus {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  min-height: 32px;
}

.screening-q__virus input {
  width: 16px;
  height: 16px;
  accent-color: var(--danger);
  cursor: pointer;
}

.inventory-eval-toggle {
  align-items: flex-start;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  display: flex;
  gap: 9px;
  padding: 10px 12px;
}

.inventory-eval-toggle input {
  margin-top: 3px;
}

.inventory-eval-toggle span,
.inventory-eval-toggle strong,
.inventory-eval-toggle small {
  display: block;
}

.inventory-eval-toggle strong {
  font-size: var(--text-meta);
}

.inventory-eval-toggle small {
  color: var(--text-secondary);
  font-size: var(--text-micro);
  line-height: 1.5;
  margin-top: 2px;
}

.smart-combobox-option--create {
  border-top: 1px solid var(--border);
  color: var(--accent);
  font-weight: 800;
}

.inv-item-row__teeth.is-warning {
  align-items: center;
  display: flex;
  justify-content: space-between;
}

/* ════════════════════════════════════════════════════════════════════
   Command Palette (Cmd+K / Ctrl+K) — ADR-287
   ════════════════════════════════════════════════════════════════════
   The palette is the app's central modal wearing a search skin: the shell
   is ResponsiveModal (`.rm-backdrop` > `.rm-card.cmdk-modal`), the search
   box rides the modal HEADER, the grouped results scroll in the BODY, and
   the keyboard hints sit in the FOOTER. Everything below styles that
   content — the surface itself is the same one every CTA modal uses.
   ════════════════════════════════════════════════════════════════════ */

/* Owner 2026-07-25: the palette's own backdrop + panel are GONE. It wears the
   central ResponsiveModal (`.rm-backdrop` > `.rm-card.cmdk-modal`), so the
   glass surface, radius, shadow, scrim, entrance/exit, phone bottom-sheet and
   swipe-to-dismiss all arrive from the one shell. What is left here is only
   what a SEARCH surface needs on top of a normal modal: the search box sits in
   the header (so it never scrolls away) and the body carries no padding of its
   own because the results list brings its own. */

.rm-card.cmdk-modal .rm-title-cluster { flex: 1; min-width: 0; }

.rm-card.cmdk-modal .rm-title-text,
.rm-card.cmdk-modal .rm-title { width: 100%; min-width: 0; font-size: inherit; font-weight: inherit; }

.rm-card.cmdk-modal .rm-body { padding: 0; }

/* ── Input row ─────────────────────────────────────────────────────── */

/* Inside the modal header the wrap is a bare flex row — the header already
   owns the padding and the divider under it. */

.rm-card.cmdk-modal .cmdk-input-wrap { padding: 0; border-bottom: 0; }

.cmdk-input-wrap {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.cmdk-input-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
  flex-shrink: 0;
}

.cmdk-input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  outline: none;
  font: inherit;
  font-size: var(--text-md);
  color: var(--text-primary);
  padding: 4px 0;
}

.cmdk-input::placeholder { color: var(--text-tertiary); }

.cmdk-input-kbd {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: var(--text-xs);
  font-family: 'JetBrains Mono', monospace;
  color: var(--text-tertiary);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

/* Server round-trip in flight. Sits where the Esc chip does so the row
   never reflows between the two states. */

.cmdk-input-spinner {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}

/* ── Results list ──────────────────────────────────────────────────── */

.cmdk-results {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-1-5);
  scrollbar-width: thin;
}

.cmdk-group + .cmdk-group { margin-top: 4px; }

.cmdk-group-title {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  padding: 6px 10px 4px;
  font-size: var(--text-xs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}

.cmdk-group-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
}

.cmdk-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 9px 12px;
  background: transparent;
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  color: var(--text-primary);
  text-align: start;
  font: inherit;
  transition: background var(--duration-fast) var(--ease-smooth);
}

.cmdk-item.is-active {
  background: var(--accent-soft);
  color: var(--accent);
}

.cmdk-item.is-active .cmdk-item-sub { color: var(--accent); opacity: 0.78; }

.cmdk-item.is-active .cmdk-item-icon { color: var(--accent); }

.cmdk-item-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-subtle);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
}

.cmdk-item.is-active .cmdk-item-icon {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.cmdk-item-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.cmdk-item-label {
  font-size: var(--text-md);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmdk-item-sub {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmdk-item-kbd {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  font-size: var(--text-xs);
  font-family: 'JetBrains Mono', monospace;
  color: var(--text-tertiary);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

.cmdk-item.is-active .cmdk-item-kbd {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-color: color-mix(in srgb, var(--accent) 24%, var(--border));
}

/* ── Empty state ───────────────────────────────────────────────────── */

.cmdk-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1-5);
  padding: 36px 16px;
  text-align: center;
}

.cmdk-empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: var(--bg-subtle);
  border-radius: 50%;
  color: var(--text-tertiary);
  margin-bottom: 4px;
}

.cmdk-empty-text {
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--text-secondary);
}

.cmdk-empty-sub {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

/* ── Footer (keyboard hints) ───────────────────────────────────────── */

.cmdk-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  padding: 8px 16px;
  border-top: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg-subtle) 60%, transparent);
  flex-shrink: 0;
}

.cmdk-footer-group {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.cmdk-footer-label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.cmdk-footer kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  padding: 1px 5px;
  font-size: var(--text-xs);
  font-family: 'JetBrains Mono', monospace;
  color: var(--text-tertiary);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
}

/* Phone: nothing here builds a sheet any more - `.rm-card` already IS the
   bottom sheet below 48em, with the same drag handle, slide-up and
   swipe-to-dismiss as every other modal. These are only the two things a
   SEARCH sheet wants on top of that. Same 47.99em edge the shell switches on. */

@media (max-width: 47.99em) {
  /* Never open as a thin strip - a search surface needs room to list. */
  .rm-card.cmdk-modal { min-height: 60dvh; }
  /* Room for the home indicator (the body's own padding is zeroed above). */
  .rm-card.cmdk-modal .cmdk-results { padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px)); }
  /* No Esc chip and no keyboard-hints footer on a touch sheet: there is no
     keyboard to hint at, and the handle already says "drag me down". */
  .rm-card.cmdk-modal .cmdk-input-kbd,
  .rm-card.cmdk-modal .cmdk-footer { display: none; }
}

/* ── Voice dictation panel (W4 Batch A / «وضع العمليات») ──────────
   Lives in styles.css, NOT in a lazy chunk: ADR-156 - a base rule moved
   into a route chunk loads AFTER the eager stylesheet and starts winning
   over same-specificity overrides that stay behind. The panel mounts in
   the patient file AND (Batch B) chair-side, so it has no single chunk
   to belong to anyway. */

/* The block dictation panel is RETIRED (W5, 2026-07-28). Its `.voice-panel*`
   family described a bordered box with its own row, hint, transcript and quota
   lines - a fourth kind of surface on a screen whose cards and heads had just
   been unified, which is why the owner called it out. The ONE mic is
   `.voice-mic-btn` below, on the central `.icon-btn` shape, and it lives in the
   row where the related action already is. */

/* The recording cue must read at a glance from arm's length - the doctor is at
   the chair, not leaning into the screen. ONE cue for the ONE mic, so the same
   red + pulse means "recording" in the chart head, the note composer and Modir. */

@keyframes voice-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* VoiceMicButton (W5) - THE mic. It borrows the central `.icon-btn` shape, so
   only the recording state and the inline timer are declared here. */

/* The mic ALWAYS stands beside a small button - Save in the chart head, «إضافة»
   in the note composer, Send in Modir's - so it is sized to one. `.icon-btn`'s
   own 38px box left it 4-6px taller than every neighbour, which is precisely
   what makes a tidy row look assembled from spare parts. Sizing the ONE mic once
   is the fix; the alternative was a height override per context, forever.
   (Inside a SectionCard head the slot's own 32px rule is more specific and
   wins - the slot owns its height there, which is correct.) */

.voice-mic-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  flex-shrink: 0;
  width: auto;
  min-width: 34px;
  height: 34px;
  padding-inline: 8px;
}

.voice-mic-btn.is-recording {
  background: var(--danger, #d64545);
  border-color: var(--danger, #d64545);
  color: #fff;
}

.voice-mic-btn.is-recording svg { animation: voice-pulse 1.4s ease-in-out infinite; }

.voice-mic-btn__timer {
  font-size: 11px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  direction: ltr;
}

@media (prefers-reduced-motion: reduce) {
  .voice-mic-btn.is-recording svg { animation: none; }
}

/* ── Voice chart confirm (W4 Batch B) ─────────────────────────────
   The per-tooth gate. Kept in styles.css (not a route chunk) for the
   ADR-156 reason: a base rule moved into a lazy chunk loads AFTER the
   eager sheet and starts beating same-specificity overrides left behind. */

.vcc {
  margin-top: 12px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 12px);
  background: var(--surface);
}

.vcc__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }

.vcc__title { font-weight: 700; font-size: 14px; }

.vcc__hint { font-size: 12px; color: var(--text-muted, var(--muted)); line-height: 1.6; margin-top: 4px; }

.vcc__list { display: grid; gap: 8px; margin-top: 10px; }

.vcc__row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-sm, 8px);
  background: var(--surface-2, var(--surface));
  flex-wrap: wrap;
}

/* A row that needs a human decision must not look like a ready row - the
   whole point of V7 is that the doctor notices it. */

.vcc__row.is-ask { border-color: var(--warning, #E0A030); }

.vcc__row.is-dup { border-color: var(--danger, #d64545); }

.vcc__row-main { flex: 1 1 240px; min-width: 0; }

.vcc__row-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.vcc__row-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

.vcc__chip {
  font-size: 11px; padding: 2px 6px; border-radius: 6px;
  background: var(--bg-subtle, var(--surface-2)); color: var(--text-secondary);
}

.vcc__code { font-weight: 700; font-size: 13px; }

.vcc__tooth { font-size: 12px; font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 700; }

.vcc__spoken {
  font-size: 12px; color: var(--text-secondary); margin-top: 4px; line-height: 1.7;
  /* Dictated free text can carry one very long unbroken token. */
  overflow-wrap: anywhere;
}

.vcc__warn {
  display: flex; align-items: center; gap: 5px; margin-top: 6px;
  font-size: 12px; color: var(--danger, #d64545); line-height: 1.6;
}

.vcc__ask {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px;
  font-size: 12px; color: var(--warning-text, var(--text-secondary));
}

.vcc__pick { max-width: 160px; }

/* ── ReorderList: THE ordered-rows primitive (ui/reorder-list.jsx) ── */

.reorder-list { display: flex; flex-direction: column; gap: 6px; width: 100%; }

.reorder-row { display: flex; align-items: center; gap: 10px; }

.reorder-arrows { display: flex; flex-direction: column; gap: 2px; flex: none; }

.reorder-btn {
  display: grid; place-items: center; width: 26px; height: 21px; padding: 0;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--surface, transparent); color: var(--text-tertiary); cursor: pointer;
  transition: color var(--duration-fast, .15s), border-color var(--duration-fast, .15s);
}

.reorder-btn:disabled { opacity: .3; cursor: default; }

.reorder-btn:not(:disabled):hover { color: var(--accent); border-color: var(--accent); }

.reorder-content { flex: 1; min-width: 0; }
